diff --git a/.travis.yml b/.travis.yml
index b8e420e48..c11f03b3b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,12 +1,12 @@
language: php
php:
- - 7.2
+ - 7.3
matrix:
fast_finish: true
include:
- - php: 7.2
+ - php: 7.3
script:
- make dev
diff --git a/src/packages/jbuniversal/jbuniversal/cart-elements/email/track/tmpl/track.php b/src/packages/jbuniversal/jbuniversal/cart-elements/email/track/tmpl/track.php
new file mode 100644
index 000000000..c7680f521
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/cart-elements/email/track/tmpl/track.php
@@ -0,0 +1,19 @@
+
+ */
+
+// no direct access
+defined('_JEXEC') or die('Restricted access');
+
+echo $order->track;
\ No newline at end of file
diff --git a/src/packages/jbuniversal/jbuniversal/cart-elements/email/track/track.php b/src/packages/jbuniversal/jbuniversal/cart-elements/email/track/track.php
new file mode 100644
index 000000000..aa3a204f3
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/cart-elements/email/track/track.php
@@ -0,0 +1,32 @@
+
+ */
+
+// no direct access
+defined('_JEXEC') or die('Restricted access');
+
+/**
+ * Class JBCartElementEmailOrderID
+ */
+class JBCartElementEmailTrack extends JBCartElementEmail
+{
+ /**
+ * @param array $params
+ * @return bool
+ */
+ public function hasValue($params = array())
+ {
+ return $this->getOrder()->track;
+ }
+}
diff --git a/src/packages/jbuniversal/jbuniversal/cart-elements/email/track/track.xml b/src/packages/jbuniversal/jbuniversal/cart-elements/email/track/track.xml
new file mode 100644
index 000000000..c9dd5a213
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/cart-elements/email/track/track.xml
@@ -0,0 +1,24 @@
+
+
+
+ JBZOO_ELEMENT_EMAIL_TRACK_NAME
+ JBZoo.com
+ 2014
+ Copyright (C) JBZoo.com
+ admin@jbzoo.com
+ http://jbzoo.com
+ 1.0
+ JBZOO_ELEMENT_EMAIL_TRACK_DESC
+
+
diff --git a/src/packages/jbuniversal/jbuniversal/cart-elements/order/checkbox/checkbox.xml b/src/packages/jbuniversal/jbuniversal/cart-elements/order/checkbox/checkbox.xml
index 01cf4786d..6ee27ecf1 100644
--- a/src/packages/jbuniversal/jbuniversal/cart-elements/order/checkbox/checkbox.xml
+++ b/src/packages/jbuniversal/jbuniversal/cart-elements/order/checkbox/checkbox.xml
@@ -20,4 +20,12 @@
http://jbzoo.com
1.0
JBZOO_ELEMENT_ORDER_CHECKBOX_DESC
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/packages/jbuniversal/jbuniversal/cart-elements/order/radio/radio.xml b/src/packages/jbuniversal/jbuniversal/cart-elements/order/radio/radio.xml
index d2a47aead..e68114f33 100644
--- a/src/packages/jbuniversal/jbuniversal/cart-elements/order/radio/radio.xml
+++ b/src/packages/jbuniversal/jbuniversal/cart-elements/order/radio/radio.xml
@@ -20,4 +20,12 @@
http://jbzoo.com
1.0
JBZOO_ELEMENT_ORDER_RADIO_DESC
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/packages/jbuniversal/jbuniversal/cart-elements/shippingfield/usercustom/tmpl/submission_textarea.php b/src/packages/jbuniversal/jbuniversal/cart-elements/shippingfield/usercustom/tmpl/submission_textarea.php
index 71f502e8e..5ec14091a 100644
--- a/src/packages/jbuniversal/jbuniversal/cart-elements/shippingfield/usercustom/tmpl/submission_textarea.php
+++ b/src/packages/jbuniversal/jbuniversal/cart-elements/shippingfield/usercustom/tmpl/submission_textarea.php
@@ -51,4 +51,4 @@
'value' => isset($userFields[$userField]) ? $userFields[$userField] : null,
);
-echo '';
+echo '';
diff --git a/src/packages/jbuniversal/jbuniversal/config/forms/config_yandexyml.xml b/src/packages/jbuniversal/jbuniversal/config/forms/config_yandexyml.xml
index c6c0a8476..7fcc4e86e 100644
--- a/src/packages/jbuniversal/jbuniversal/config/forms/config_yandexyml.xml
+++ b/src/packages/jbuniversal/jbuniversal/config/forms/config_yandexyml.xml
@@ -47,40 +47,18 @@
-
+
-
+
-
+
-
-
-
-
-
-
-
-
-
-
- Не содержит в поле данное значение
- Только items с данным значением
-
-
-
-
-
-
-
+
-
\ No newline at end of file
+
diff --git a/src/packages/jbuniversal/jbuniversal/config/forms/subforms/yandexyml.xml b/src/packages/jbuniversal/jbuniversal/config/forms/subforms/yandexyml.xml
new file mode 100644
index 000000000..35c84068e
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/config/forms/subforms/yandexyml.xml
@@ -0,0 +1,31 @@
+
+
+
diff --git a/src/packages/jbuniversal/jbuniversal/framework/classes/cart/jborder.php b/src/packages/jbuniversal/jbuniversal/framework/classes/cart/jborder.php
index d5eadedb8..0068cbfdb 100644
--- a/src/packages/jbuniversal/jbuniversal/framework/classes/cart/jborder.php
+++ b/src/packages/jbuniversal/jbuniversal/framework/classes/cart/jborder.php
@@ -46,6 +46,11 @@ class JBCartOrder
*/
public $comment;
+ /**
+ * @var string
+ */
+ public $track;
+
/**
* @var ParameterData
*/
@@ -1099,6 +1104,7 @@ public function setData($data)
$this->modified = $data->get('modified');
$this->created_by = $data->get('created_by');
$this->comment = $data->get('comment');
+ $this->track = $data->get('track');
$this->params = $this->app->data->create($data->get('params'));
$this->setStatus($data->get('status'));
@@ -1253,8 +1259,10 @@ public function updateData($data)
}
}
- $this->comment = $data->get('comment');
+ $this->comment = $data->get('comment');
+
$this->setStatus($data->get('status'), JBCart::STATUS_ORDER);
+ $this->setTrack($data->get('track'));
}
/**
@@ -1318,6 +1326,33 @@ public function setStatusList(array $statList)
}
+ /**
+ * Set the Order track
+ * @param string $track The new Order track
+ * @return $this
+ */
+ public function setTrack($track)
+ {
+ $newTrack = JString::trim($track);
+ $oldTrack = $this->track;
+
+ $this->track = $newTrack;
+
+ if (empty($oldTrack) && !empty($newTrack)) {
+ $this->app->jbevent->fire($this, 'basket:addTrack', array(
+ 'track' => (string) $newTrack,
+ ));
+ }
+
+ if (!empty($track) && (string)$oldTrack != (string)$newTrack) {
+ $this->app->jbevent->fire($this, 'basket:changeTrack', array(
+ 'track' => (string) $newTrack,
+ ));
+ }
+
+ return $this;
+ }
+
}
/**
diff --git a/src/packages/jbuniversal/jbuniversal/framework/classes/jbmodulehelper.php b/src/packages/jbuniversal/jbuniversal/framework/classes/jbmodulehelper.php
index d02e1f2e0..55a7e322a 100644
--- a/src/packages/jbuniversal/jbuniversal/framework/classes/jbmodulehelper.php
+++ b/src/packages/jbuniversal/jbuniversal/framework/classes/jbmodulehelper.php
@@ -76,6 +76,10 @@ public function __construct($params, $module)
$this->app = App::getInstance('zoo');
+ // register helper path
+ $template = $this->app->zoo->getApplication()->getTemplate()->name;
+ $this->app->jbtemplate->regHelpersByTpl($template);
+
// vars
$this->_params = $params;
$this->_module = $module;
@@ -262,4 +266,4 @@ public function partial($layout = null, $vars = [])
return null;
}
-}
\ No newline at end of file
+}
diff --git a/src/packages/jbuniversal/jbuniversal/framework/events/jbevent.basket.php b/src/packages/jbuniversal/jbuniversal/framework/events/jbevent.basket.php
index c2bb0791b..6888d6b8b 100644
--- a/src/packages/jbuniversal/jbuniversal/framework/events/jbevent.basket.php
+++ b/src/packages/jbuniversal/jbuniversal/framework/events/jbevent.basket.php
@@ -157,4 +157,20 @@ public static function recount($event)
self::app()->jbevent->fireElements($event);
}
+ /**
+ * @param AppEvent $event
+ */
+ public static function addTrack($event)
+ {
+ self::app()->jbevent->fireElements($event);
+ }
+
+ /**
+ * @param AppEvent $event
+ */
+ public static function changeTrack($event)
+ {
+ self::app()->jbevent->fireElements($event);
+ }
+
}
diff --git a/src/packages/jbuniversal/jbuniversal/framework/helpers/jbevent.php b/src/packages/jbuniversal/jbuniversal/framework/helpers/jbevent.php
index 204ddc192..cb81fef59 100644
--- a/src/packages/jbuniversal/jbuniversal/framework/helpers/jbevent.php
+++ b/src/packages/jbuniversal/jbuniversal/framework/helpers/jbevent.php
@@ -106,6 +106,13 @@ class JBEventHelper extends AppHelper
'name' => 'order_removeitems',
),
+ // order track
+ 'basket:addtrack' => array(
+ 'name' => 'order_addtrack',
+ ),
+ 'basket:changetrack' => array(
+ 'name' => 'order_changetrack',
+ ),
);
/**
@@ -186,7 +193,7 @@ public function fireElements(AppEvent $event)
* @param array $params
*/
protected function _execElements($event, $elements, $order, $params)
- {
+ {
$elements = (array)$elements;
if (empty($elements)) {
return;
diff --git a/src/packages/jbuniversal/jbuniversal/framework/helpers/jbless.php b/src/packages/jbuniversal/jbuniversal/framework/helpers/jbless.php
index dd7a81741..4de5fe9b6 100644
--- a/src/packages/jbuniversal/jbuniversal/framework/helpers/jbless.php
+++ b/src/packages/jbuniversal/jbuniversal/framework/helpers/jbless.php
@@ -142,7 +142,9 @@ protected function _getHash($mainPath)
protected function _getProcessor($addPath = array())
{
if (!class_exists('Less_Parser')) {
- require_once JPATH_ROOT . '/media/zoo/applications/jbuniversal/framework/libs/less.gpeasy.php';
+ require_once JPATH_ROOT . '/media/zoo/applications/jbuniversal/framework/libs/lessphp/lib/Less/Autoloader.php';
+
+ Less_Autoloader::register();
}
$options = array(
diff --git a/src/packages/jbuniversal/jbuniversal/framework/helpers/jbordermacros.php b/src/packages/jbuniversal/jbuniversal/framework/helpers/jbordermacros.php
index 4a2499804..46009d0ec 100644
--- a/src/packages/jbuniversal/jbuniversal/framework/helpers/jbordermacros.php
+++ b/src/packages/jbuniversal/jbuniversal/framework/helpers/jbordermacros.php
@@ -42,6 +42,7 @@ class JBOrderMacrosHelper extends AppHelper
'order_shipping_name',
'order_shipping_elem',
'order_shipping_stat',
+ 'order_track',
'user_id',
'user_name',
@@ -236,6 +237,8 @@ private function _replaceMacros($text, $macros, JBCartOrder $order = null)
}
}
+ } else if ($macros == 'order_track') {
+ $replace = $order->track;
} else {
throw new Exception('Undefined email macros: "{' . $macros . '}"');
}
diff --git a/src/packages/jbuniversal/jbuniversal/framework/helpers/jbrouter.php b/src/packages/jbuniversal/jbuniversal/framework/helpers/jbrouter.php
index 866cf19f9..db0742099 100644
--- a/src/packages/jbuniversal/jbuniversal/framework/helpers/jbrouter.php
+++ b/src/packages/jbuniversal/jbuniversal/framework/helpers/jbrouter.php
@@ -693,27 +693,25 @@ private function _url(array $params = [], $zooRoute = false, $base = 'index.php'
*/
public function externalItem(Item $item)
{
- $app = JFactory::getApplication();
- $isSsl = $app::getRouter()->build('')->isSsl();
+ $app = JFactory::getApplication();
+ $ssl = (JFactory::getConfig()->get('force_ssl', 0) == 2) ? 1 : -1;
if ($this->app->jbenv->isSite()) {
- $url = JRoute::_($this->app->route->item($item, false), false, 2); // force always full url
- if ($isSsl) {
- $url = str_replace('http://', 'https://', $url);
- }
+ $url = JRoute::_($this->app->route->item($item, false), false, $ssl); // force always full url
+
return $url;
} else {
$root = JUri::root();
$application = JApplication::getInstance('site');
$router = $application->getRouter();
- $link = $router->build($this->app->route->item($item, false));
+ $link = $router->build($this->app->route->item($item, false), false, $ssl)->toString();
if (JBModelConfig::model()->getGroup('config')->get('sef.fix_item')) {
$link = preg_replace('#\/item\/#', '/', '' . $link, 1);
}
- return $root . preg_replace('/^.*administrator\//', '', $link, 1);
+ return rtrim($root, '/') . preg_replace('/^.*administrator\//', '', $link, 1);
}
}
diff --git a/src/packages/jbuniversal/jbuniversal/framework/helpers/jbtables.php b/src/packages/jbuniversal/jbuniversal/framework/helpers/jbtables.php
index d7eea4cad..17eeaf74e 100644
--- a/src/packages/jbuniversal/jbuniversal/framework/helpers/jbtables.php
+++ b/src/packages/jbuniversal/jbuniversal/framework/helpers/jbtables.php
@@ -237,6 +237,8 @@ public function checkOrder($force = false)
if (!isset($checked) || $force) {
+ // Create Order Table
+
$this->createTable(ZOO_TABLE_JBZOO_ORDER, array(
'`id` INT(11) NOT NULL AUTO_INCREMENT',
'`status` VARCHAR(100) NULL DEFAULT \'0\'',
@@ -265,6 +267,33 @@ public function checkOrder($force = false)
'INDEX `total` (`total`)',
'FULLTEXT INDEX `comment` (`comment`)',
));
+
+ // Add Track Column
+
+ if ($fields = $this->getTableInfo(ZOO_TABLE_JBZOO_ORDER)) {
+
+ // Check Track Column
+ if (!isset($fields['track'])) {
+ $sql = 'ALTER TABLE `' . ZOO_TABLE_JBZOO_ORDER . '` ADD COLUMN `track` TEXT NULL AFTER `shippingfields`';
+ $this->_query($sql);
+ }
+ }
+
+ // Create Tracking Table
+
+ // $this->createTable(ZOO_TABLE_JBZOO_TRACKING, array(
+ // '`id` INT(11) NOT NULL AUTO_INCREMENT',
+ // '`order_id` INT(11) NULL DEFAULT \'0\'',
+ // '`state` VARCHAR(100) NULL DEFAULT \'0\'',
+ // '`date` DATETIME NULL DEFAULT NULL',
+ // '`comment` TEXT NULL',
+ // ), array(
+ // 'PRIMARY KEY (`id`)',
+ // 'INDEX `order_id` (`order_id`)',
+ // 'INDEX `state` (`state`)',
+ // 'INDEX `date` (`date`)',
+ // 'FULLTEXT INDEX `comment` (`comment`)',
+ // ));
}
$checked = true;
diff --git a/src/packages/jbuniversal/jbuniversal/framework/helpers/jbyml.php b/src/packages/jbuniversal/jbuniversal/framework/helpers/jbyml.php
index 67142f29c..894c13a17 100644
--- a/src/packages/jbuniversal/jbuniversal/framework/helpers/jbyml.php
+++ b/src/packages/jbuniversal/jbuniversal/framework/helpers/jbyml.php
@@ -417,12 +417,15 @@ public function renderStart()
*/
public function exportItems($offset, $limit)
{
- $types = $this->_appParams->get('type_list');
+ $types = $this->_appParams->get('type_list');
+
$items = JBModelItem::model()->getList(
$this->_appParams->get('app_list'),
null,
$types,
array(
+ 'id' => $this->_appParams->get('enable_rules', 0) ? $this->_appParams->get('include', '') : '',
+ 'elements' => $this->_appParams->get('enable_rules', 0) ? $this->_appParams->get('condition', array()) : '',
'limit' => array($offset, $limit),
'published' => 1,
)
@@ -697,7 +700,7 @@ public function replaceSpecial($text)
* @return int|boolean
*/
public function getTotal()
- {
+ {
$types = $this->_appParams->get('type_list');
$appId = $this->_appParams->get('app_list');
@@ -705,6 +708,17 @@ public function getTotal()
return false;
}
- return JBModelItem::model()->getTotal($appId, $types);
+ $count = JBModelItem::model()->getListCount(
+ $appId,
+ null,
+ $types,
+ array(
+ 'id' => $this->_appParams->get('enable_rules', 0) ? $this->_appParams->get('include', '') : '',
+ 'elements' => $this->_appParams->get('enable_rules', 0) ? $this->_appParams->get('condition', array()) : '',
+ 'published' => 1,
+ )
+ );
+
+ return $count;
}
}
diff --git a/src/packages/jbuniversal/jbuniversal/framework/jbzoo.php b/src/packages/jbuniversal/jbuniversal/framework/jbzoo.php
index 242860e9a..170001c24 100644
--- a/src/packages/jbuniversal/jbuniversal/framework/jbzoo.php
+++ b/src/packages/jbuniversal/jbuniversal/framework/jbzoo.php
@@ -120,6 +120,7 @@ private function _initClasses()
define('ZOO_TABLE_JBZOO_FAVORITE', '#__zoo_jbzoo_favorite');
define('ZOO_TABLE_JBZOO_CONFIG', '#__zoo_jbzoo_config');
define('ZOO_TABLE_JBZOO_ORDER', '#__zoo_jbzoo_orders');
+ define('ZOO_TABLE_JBZOO_TRACKING', '#__zoo_jbzoo_tracking');
$frameworkPath = JPATH_SITE . '/media/zoo/applications/jbuniversal/framework';
$clsPath = $frameworkPath . '/classes';
@@ -383,6 +384,9 @@ private function _initEvents()
$dispatcher->connect('basket:paymentSuccess', ['JBEventBasket', 'paymentSuccess']);
$dispatcher->connect('basket:paymentFail', ['JBEventBasket', 'paymentFail']);
$dispatcher->connect('basket:paymentCallback', ['JBEventBasket', 'paymentCallback']);
+
+ $dispatcher->connect('basket:addTrack', ['JBEventBasket', 'addTrack']);
+ $dispatcher->connect('basket:changeTrack', ['JBEventBasket', 'changeTrack']);
}
/**
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/less.gpeasy.php b/src/packages/jbuniversal/jbuniversal/framework/libs/less.gpeasy.php
deleted file mode 100644
index 5f8c75115..000000000
--- a/src/packages/jbuniversal/jbuniversal/framework/libs/less.gpeasy.php
+++ /dev/null
@@ -1,11161 +0,0 @@
- '/');
- }
-
-
- // generate name for compiled css file
- $hash = md5(json_encode($less_files));
- $list_file = Less_Cache::$cache_dir . 'lessphp_' . $hash . '.list';
-
-
- // check cached content
- if (!isset($parser_options['use_cache']) || $parser_options['use_cache'] === true) {
- if (file_exists($list_file)) {
-
- $list = explode("\n", file_get_contents($list_file));
-
- //pop the cached name that should match $compiled_name
- $cached_name = array_pop($list);
- if (!preg_match('/^lessphp_[a-f0-9]+\.css$/', $cached_name)) {
- $list[] = $cached_name;
- $cached_name = false;
- }
- $compiled_name = self::CompiledName($list);
-
- // if $cached_name != $compiled_name, we know we need to recompile
- if (!$cached_name || $cached_name === $compiled_name) {
-
- $output_file = self::OutputFile($compiled_name, $parser_options);
-
- if ($output_file && file_exists($output_file)) {
- @touch($list_file);
- @touch($output_file);
- return basename($output_file); // for backwards compatibility, we just return the name of the file
- }
- }
- }
- }
-
- $compiled = self::Cache($less_files, $parser_options);
- if (!$compiled) {
- return false;
- }
-
- $compiled_name = self::CompiledName($less_files);
- $output_file = self::OutputFile($compiled_name, $parser_options);
-
-
- //save the file list
- $list = $less_files;
- $list[] = $compiled_name;
- $cache = implode("\n", $list);
- file_put_contents($list_file, $cache);
-
-
- //save the css
- file_put_contents($output_file, $compiled);
-
-
- //clean up
- self::CleanCache();
-
- return basename($output_file);
- }
-
- /**
- * Force the compiler to regenerate the cached css file
- *
- * @param array $less_files Array of .less files to compile
- * @param array $parser_options Array of compiler options
- * @param array $modify_vars Array of variables
- * @return string Name of the css file
- */
- public static function Regen($less_files, $parser_options = array(), $modify_vars = array())
- {
- $parser_options['use_cache'] = false;
- return self::Get($less_files, $parser_options, $modify_vars);
- }
-
- public static function Cache(&$less_files, $parser_options = array())
- {
-
-
- // get less.php if it exists
- $file = dirname(__FILE__) . '/Less.php';
- if (file_exists($file) && !class_exists('Less_Parser')) {
- require_once($file);
- }
-
- $parser_options['cache_dir'] = Less_Cache::$cache_dir;
- $parser = new Less_Parser($parser_options);
-
-
- // combine files
- foreach ($less_files as $file_path => $uri_or_less) {
-
- //treat as less markup if there are newline characters
- if (strpos($uri_or_less, "\n") !== false) {
- $parser->Parse($uri_or_less);
- continue;
- }
-
- $parser->ParseFile($file_path, $uri_or_less);
- }
-
- $compiled = $parser->getCss();
-
-
- $less_files = $parser->allParsedFiles();
-
- return $compiled;
- }
-
-
- private static function OutputFile($compiled_name, $parser_options)
- {
-
- //custom output file
- if (!empty($parser_options['output'])) {
-
- //relative to cache directory?
- if (preg_match('#[\\\\/]#', $parser_options['output'])) {
- return $parser_options['output'];
- }
-
- return Less_Cache::$cache_dir . $parser_options['output'];
- }
-
- return Less_Cache::$cache_dir . $compiled_name;
- }
-
-
- private static function CompiledName($files)
- {
-
- //save the file list
- $temp = array(Less_Version::cache_version);
- foreach ($files as $file) {
- $temp[] = filemtime($file) . "\t" . filesize($file) . "\t" . $file;
- }
-
- return 'lessphp_' . sha1(json_encode($temp)) . '.css';
- }
-
-
- public static function SetCacheDir($dir)
- {
- Less_Cache::$cache_dir = $dir;
- }
-
- public static function CheckCacheDir()
- {
-
- Less_Cache::$cache_dir = str_replace('\\', '/', Less_Cache::$cache_dir);
- Less_Cache::$cache_dir = rtrim(Less_Cache::$cache_dir, '/') . '/';
-
- if (!file_exists(Less_Cache::$cache_dir)) {
- if (!mkdir(Less_Cache::$cache_dir)) {
- throw new Less_Exception_Parser('Less.php cache directory couldn\'t be created: ' . Less_Cache::$cache_dir);
- }
-
- } elseif (!is_dir(Less_Cache::$cache_dir)) {
- throw new Less_Exception_Parser('Less.php cache directory doesn\'t exist: ' . Less_Cache::$cache_dir);
-
- } elseif (!is_writable(Less_Cache::$cache_dir)) {
- throw new Less_Exception_Parser('Less.php cache directory isn\'t writable: ' . Less_Cache::$cache_dir);
-
- }
-
- }
-
-
- public static function CleanCache()
- {
- static $clean = false;
-
- if ($clean) {
- return;
- }
-
- $files = scandir(Less_Cache::$cache_dir);
- if ($files) {
- $check_time = time() - 604800;
- foreach ($files as $file) {
- if (strpos($file, 'lessphp_') !== 0) {
- continue;
- }
- $full_path = Less_Cache::$cache_dir . '/' . $file;
- if (filemtime($full_path) > $check_time) {
- continue;
- }
- unlink($full_path);
- }
- }
-
- $clean = true;
- }
-
-}
-
-/**
- * Class for parsing and compiling less files into css
- *
- * @package Less
- * @subpackage parser
- *
- */
-class Less_Parser
-{
- /**
- * Default parser options
- */
- public static $default_options = array(
- 'compress' => false, // option - whether to compress
- 'strictUnits' => false, // whether units need to evaluate correctly
- 'strictMath' => false, // whether math has to be within parenthesis
- 'relativeUrls' => true, // option - whether to adjust URL's to be relative
- 'urlArgs' => array(), // whether to add args into url tokens
- 'numPrecision' => 8,
-
- 'import_dirs' => array(),
- 'import_callback' => null,
- 'cache_dir' => null,
- 'cache_method' => 'php', // false, 'serialize', 'php', 'var_export', 'callback';
- 'cache_callback_get' => null,
- 'cache_callback_set' => null,
-
- 'sourceMap' => false, // whether to output a source map
- 'sourceMapBasepath' => null,
- 'sourceMapWriteTo' => null,
- 'sourceMapURL' => null,
-
- 'plugins' => array(),
-
- );
-
- public static $options = array();
-
-
- private $input; // Less input string
- private $input_len; // input string length
- private $pos; // current index in `input`
- private $saveStack = array(); // holds state for backtracking
- private $furthest;
-
- /**
- * @var Less_Environment
- */
- private $env;
-
- private $rules = array();
-
- private static $imports = array();
-
- public static $has_extends = false;
-
- public static $next_id = 0;
-
- /**
- * Filename to contents of all parsed the files
- *
- * @var array
- */
- public static $contentsMap = array();
-
-
- /**
- * @param Less_Environment|array|null $env
- */
- public function __construct($env = null)
- {
-
- // Top parser on an import tree must be sure there is one "env"
- // which will then be passed around by reference.
- if ($env instanceof Less_Environment) {
- $this->env = $env;
- } else {
- $this->SetOptions(Less_Parser::$default_options);
- $this->Reset($env);
- }
-
- }
-
-
- /**
- * Reset the parser state completely
- *
- */
- public function Reset($options = null)
- {
- $this->rules = array();
- self::$imports = array();
- self::$has_extends = false;
- self::$imports = array();
- self::$contentsMap = array();
-
- $this->env = new Less_Environment($options);
- $this->env->Init();
-
- //set new options
- if (is_array($options)) {
- $this->SetOptions(Less_Parser::$default_options);
- $this->SetOptions($options);
- }
- }
-
- /**
- * Set one or more compiler options
- * options: import_dirs, cache_dir, cache_method
- *
- */
- public function SetOptions($options)
- {
- foreach ($options as $option => $value) {
- $this->SetOption($option, $value);
- }
- }
-
- /**
- * Set one compiler option
- *
- */
- public function SetOption($option, $value)
- {
-
- switch ($option) {
-
- case 'import_dirs':
- $this->SetImportDirs($value);
- return;
-
- case 'cache_dir':
- if (is_string($value)) {
- Less_Cache::SetCacheDir($value);
- Less_Cache::CheckCacheDir();
- }
- return;
- }
-
- Less_Parser::$options[$option] = $value;
- }
-
- /**
- * Registers a new custom function
- *
- * @param string $name function name
- * @param callable $callback callback
- */
- public function registerFunction($name, $callback)
- {
- $this->env->functions[$name] = $callback;
- }
-
- /**
- * Removed an already registered function
- *
- * @param string $name function name
- */
- public function unregisterFunction($name)
- {
- if (isset($this->env->functions[$name]))
- unset($this->env->functions[$name]);
- }
-
-
- /**
- * Get the current css buffer
- *
- * @return string
- */
- public function getCss()
- {
-
- $precision = ini_get('precision');
- @ini_set('precision', 16);
- $locale = setlocale(LC_NUMERIC, 0);
- setlocale(LC_NUMERIC, "C");
-
-
- $root = new Less_Tree_Ruleset(array(), $this->rules);
- $root->root = true;
- $root->firstRoot = true;
-
-
- $this->PreVisitors($root);
-
- self::$has_extends = false;
- $evaldRoot = $root->compile($this->env);
-
-
- $this->PostVisitors($evaldRoot);
-
- if (Less_Parser::$options['sourceMap']) {
- $generator = new Less_SourceMap_Generator($evaldRoot, Less_Parser::$contentsMap, Less_Parser::$options);
- // will also save file
- // FIXME: should happen somewhere else?
- $css = $generator->generateCSS();
- } else {
- $css = $evaldRoot->toCSS();
- }
-
- if (Less_Parser::$options['compress']) {
- $css = preg_replace('/(^(\s)+)|((\s)+$)/', '', $css);
- }
-
- //reset php settings
- @ini_set('precision', $precision);
- setlocale(LC_NUMERIC, $locale);
-
- return $css;
- }
-
- /**
- * Run pre-compile visitors
- *
- */
- private function PreVisitors($root)
- {
-
- if (Less_Parser::$options['plugins']) {
- foreach (Less_Parser::$options['plugins'] as $plugin) {
- if (!empty($plugin->isPreEvalVisitor)) {
- $plugin->run($root);
- }
- }
- }
- }
-
-
- /**
- * Run post-compile visitors
- *
- */
- private function PostVisitors($evaldRoot)
- {
-
- $visitors = array();
- $visitors[] = new Less_Visitor_joinSelector();
- if (self::$has_extends) {
- $visitors[] = new Less_Visitor_processExtends();
- }
- $visitors[] = new Less_Visitor_toCSS();
-
-
- if (Less_Parser::$options['plugins']) {
- foreach (Less_Parser::$options['plugins'] as $plugin) {
- if (property_exists($plugin, 'isPreEvalVisitor') && $plugin->isPreEvalVisitor) {
- continue;
- }
-
- if (property_exists($plugin, 'isPreVisitor') && $plugin->isPreVisitor) {
- array_unshift($visitors, $plugin);
- } else {
- $visitors[] = $plugin;
- }
- }
- }
-
-
- for ($i = 0; $i < count($visitors); $i++) {
- $visitors[$i]->run($evaldRoot);
- }
-
- }
-
-
- /**
- * Parse a Less string into css
- *
- * @param string $str The string to convert
- * @param string $uri_root The url of the file
- * @return Less_Tree_Ruleset|Less_Parser
- */
- public function parse($str, $file_uri = null)
- {
-
- if (!$file_uri) {
- $uri_root = '';
- $filename = 'anonymous-file-' . Less_Parser::$next_id++ . '.less';
- } else {
- $file_uri = self::WinPath($file_uri);
- $filename = basename($file_uri);
- $uri_root = dirname($file_uri);
- }
-
- $previousFileInfo = $this->env->currentFileInfo;
- $uri_root = self::WinPath($uri_root);
- $this->SetFileInfo($filename, $uri_root);
-
- $this->input = $str;
- $this->_parse();
-
- if ($previousFileInfo) {
- $this->env->currentFileInfo = $previousFileInfo;
- }
-
- return $this;
- }
-
-
- /**
- * Parse a Less string from a given file
- *
- * @throws Less_Exception_Parser
- * @param string $filename The file to parse
- * @param string $uri_root The url of the file
- * @param bool $returnRoot Indicates whether the return value should be a css string a root node
- * @return Less_Tree_Ruleset|Less_Parser
- */
- public function parseFile($filename, $uri_root = '', $returnRoot = false)
- {
-
- if (!file_exists($filename)) {
- $this->Error(sprintf('File `%s` not found.', $filename));
- }
-
-
- // fix uri_root?
- // Instead of The mixture of file path for the first argument and directory path for the second argument has bee
- if (!$returnRoot && !empty($uri_root) && basename($uri_root) == basename($filename)) {
- $uri_root = dirname($uri_root);
- }
-
-
- $previousFileInfo = $this->env->currentFileInfo;
- $filename = self::WinPath($filename);
- $uri_root = self::WinPath($uri_root);
- $this->SetFileInfo($filename, $uri_root);
-
- self::AddParsedFile($filename);
-
- if ($returnRoot) {
- $rules = $this->GetRules($filename);
- $return = new Less_Tree_Ruleset(array(), $rules);
- } else {
- $this->_parse($filename);
- $return = $this;
- }
-
- if ($previousFileInfo) {
- $this->env->currentFileInfo = $previousFileInfo;
- }
-
- return $return;
- }
-
-
- /**
- * Allows a user to set variables values
- * @param array $vars
- * @return Less_Parser
- */
- public function ModifyVars($vars)
- {
-
- $this->input = Less_Parser::serializeVars($vars);
- $this->_parse();
-
- return $this;
- }
-
-
- /**
- * @param string $filename
- */
- public function SetFileInfo($filename, $uri_root = '')
- {
-
- $filename = Less_Environment::normalizePath($filename);
- $dirname = preg_replace('/[^\/\\\\]*$/', '', $filename);
-
- if (!empty($uri_root)) {
- $uri_root = rtrim($uri_root, '/') . '/';
- }
-
- $currentFileInfo = array();
-
- //entry info
- if (isset($this->env->currentFileInfo)) {
- $currentFileInfo['entryPath'] = $this->env->currentFileInfo['entryPath'];
- $currentFileInfo['entryUri'] = $this->env->currentFileInfo['entryUri'];
- $currentFileInfo['rootpath'] = $this->env->currentFileInfo['rootpath'];
-
- } else {
- $currentFileInfo['entryPath'] = $dirname;
- $currentFileInfo['entryUri'] = $uri_root;
- $currentFileInfo['rootpath'] = $dirname;
- }
-
- $currentFileInfo['currentDirectory'] = $dirname;
- $currentFileInfo['currentUri'] = $uri_root . basename($filename);
- $currentFileInfo['filename'] = $filename;
- $currentFileInfo['uri_root'] = $uri_root;
-
-
- //inherit reference
- if (isset($this->env->currentFileInfo['reference']) && $this->env->currentFileInfo['reference']) {
- $currentFileInfo['reference'] = true;
- }
-
- $this->env->currentFileInfo = $currentFileInfo;
- }
-
-
- /**
- * @deprecated 1.5.1.2
- *
- */
- public function SetCacheDir($dir)
- {
-
- if (!file_exists($dir)) {
- if (mkdir($dir)) {
- return true;
- }
- throw new Less_Exception_Parser('Less.php cache directory couldn\'t be created: ' . $dir);
-
- } elseif (!is_dir($dir)) {
- throw new Less_Exception_Parser('Less.php cache directory doesn\'t exist: ' . $dir);
-
- } elseif (!is_writable($dir)) {
- throw new Less_Exception_Parser('Less.php cache directory isn\'t writable: ' . $dir);
-
- } else {
- $dir = self::WinPath($dir);
- Less_Cache::$cache_dir = rtrim($dir, '/') . '/';
- return true;
- }
- }
-
-
- /**
- * Set a list of directories or callbacks the parser should use for determining import paths
- *
- * @param array $dirs
- */
- public function SetImportDirs($dirs)
- {
- Less_Parser::$options['import_dirs'] = array();
-
- foreach ($dirs as $path => $uri_root) {
-
- $path = self::WinPath($path);
- if (!empty($path)) {
- $path = rtrim($path, '/') . '/';
- }
-
- if (!is_callable($uri_root)) {
- $uri_root = self::WinPath($uri_root);
- if (!empty($uri_root)) {
- $uri_root = rtrim($uri_root, '/') . '/';
- }
- }
-
- Less_Parser::$options['import_dirs'][$path] = $uri_root;
- }
- }
-
- /**
- * @param string $file_path
- */
- private function _parse($file_path = null)
- {
- $this->rules = array_merge($this->rules, $this->GetRules($file_path));
- }
-
-
- /**
- * Return the results of parsePrimary for $file_path
- * Use cache and save cached results if possible
- *
- * @param string|null $file_path
- */
- private function GetRules($file_path)
- {
-
- $this->SetInput($file_path);
-
- $cache_file = $this->CacheFile($file_path);
- if ($cache_file) {
- if (Less_Parser::$options['cache_method'] == 'callback') {
- if (is_callable(Less_Parser::$options['cache_callback_get'])) {
- $cache = call_user_func_array(
- Less_Parser::$options['cache_callback_get'],
- array($this, $file_path, $cache_file)
- );
-
- if ($cache) {
- $this->UnsetInput();
- return $cache;
- }
- }
-
- } elseif (file_exists($cache_file)) {
- switch (Less_Parser::$options['cache_method']) {
-
- // Using serialize
- // Faster but uses more memory
- case 'serialize':
- $cache = unserialize(file_get_contents($cache_file));
- if ($cache) {
- touch($cache_file);
- $this->UnsetInput();
- return $cache;
- }
- break;
-
-
- // Using generated php code
- case 'var_export':
- case 'php':
- $this->UnsetInput();
- return include($cache_file);
- }
- }
- }
-
- $rules = $this->parsePrimary();
-
- if ($this->pos < $this->input_len) {
- throw new Less_Exception_Chunk($this->input, null, $this->furthest, $this->env->currentFileInfo);
- }
-
- $this->UnsetInput();
-
-
- //save the cache
- if ($cache_file) {
- if (Less_Parser::$options['cache_method'] == 'callback') {
- if (is_callable(Less_Parser::$options['cache_callback_set'])) {
- call_user_func_array(
- Less_Parser::$options['cache_callback_set'],
- array($this, $file_path, $cache_file, $rules)
- );
- }
-
- } else {
- //msg('write cache file');
- switch (Less_Parser::$options['cache_method']) {
- case 'serialize':
- file_put_contents($cache_file, serialize($rules));
- break;
- case 'php':
- file_put_contents($cache_file, '');
- break;
- case 'var_export':
- //Requires __set_state()
- file_put_contents($cache_file, '');
- break;
- }
-
- Less_Cache::CleanCache();
- }
- }
-
- return $rules;
- }
-
-
- /**
- * Set up the input buffer
- *
- */
- public function SetInput($file_path)
- {
-
- if ($file_path) {
- $this->input = file_get_contents($file_path);
- }
-
- $this->pos = $this->furthest = 0;
-
- // Remove potential UTF Byte Order Mark
- $this->input = preg_replace('/\\G\xEF\xBB\xBF/', '', $this->input);
- $this->input_len = strlen($this->input);
-
-
- if (Less_Parser::$options['sourceMap'] && $this->env->currentFileInfo) {
- $uri = $this->env->currentFileInfo['currentUri'];
- Less_Parser::$contentsMap[$uri] = $this->input;
- }
-
- }
-
-
- /**
- * Free up some memory
- *
- */
- public function UnsetInput()
- {
- unset($this->input, $this->pos, $this->input_len, $this->furthest);
- $this->saveStack = array();
- }
-
-
- public function CacheFile($file_path)
- {
-
- if ($file_path && $this->CacheEnabled()) {
-
- $env = get_object_vars($this->env);
- unset($env['frames']);
-
- $parts = array();
- $parts[] = $file_path;
- $parts[] = filesize($file_path);
- $parts[] = filemtime($file_path);
- $parts[] = $env;
- $parts[] = Less_Version::cache_version;
- $parts[] = Less_Parser::$options['cache_method'];
- return Less_Cache::$cache_dir . 'lessphp_' . base_convert(sha1(json_encode($parts)), 16, 36) . '.lesscache';
- }
- }
-
-
- static function AddParsedFile($file)
- {
- self::$imports[] = $file;
- }
-
- static function AllParsedFiles()
- {
- return self::$imports;
- }
-
- /**
- * @param string $file
- */
- static function FileParsed($file)
- {
- return in_array($file, self::$imports);
- }
-
-
- function save()
- {
- $this->saveStack[] = $this->pos;
- }
-
- private function restore()
- {
- $this->pos = array_pop($this->saveStack);
- }
-
- private function forget()
- {
- array_pop($this->saveStack);
- }
-
-
- private function isWhitespace($offset = 0)
- {
- return preg_match('/\s/', $this->input[$this->pos + $offset]);
- }
-
- /**
- * Parse from a token, regexp or string, and move forward if match
- *
- * @param array $toks
- * @return array
- */
- private function match($toks)
- {
-
- // The match is confirmed, add the match length to `this::pos`,
- // and consume any extra white-space characters (' ' || '\n')
- // which come after that. The reason for this is that LeSS's
- // grammar is mostly white-space insensitive.
- //
-
- foreach ($toks as $tok) {
-
- $char = $tok[0];
-
- if ($char === '/') {
- $match = $this->MatchReg($tok);
-
- if ($match) {
- return count($match) === 1 ? $match[0] : $match;
- }
-
- } elseif ($char === '#') {
- $match = $this->MatchChar($tok[1]);
-
- } else {
- // Non-terminal, match using a function call
- $match = $this->$tok();
-
- }
-
- if ($match) {
- return $match;
- }
- }
- }
-
- /**
- * @param string[] $toks
- *
- * @return string
- */
- private function MatchFuncs($toks)
- {
-
- if ($this->pos < $this->input_len) {
- foreach ($toks as $tok) {
- $match = $this->$tok();
- if ($match) {
- return $match;
- }
- }
- }
-
- }
-
- // Match a single character in the input,
- private function MatchChar($tok)
- {
- if (($this->pos < $this->input_len) && ($this->input[$this->pos] === $tok)) {
- $this->skipWhitespace(1);
- return $tok;
- }
- }
-
- // Match a regexp from the current start point
- private function MatchReg($tok)
- {
-
- if (preg_match($tok, $this->input, $match, 0, $this->pos)) {
- $this->skipWhitespace(strlen($match[0]));
- return $match;
- }
- }
-
-
- /**
- * Same as match(), but don't change the state of the parser,
- * just return the match.
- *
- * @param string $tok
- * @return integer
- */
- public function PeekReg($tok)
- {
- return preg_match($tok, $this->input, $match, 0, $this->pos);
- }
-
- /**
- * @param string $tok
- */
- public function PeekChar($tok)
- {
- //return ($this->input[$this->pos] === $tok );
- return ($this->pos < $this->input_len) && ($this->input[$this->pos] === $tok);
- }
-
-
- /**
- * @param integer $length
- */
- public function skipWhitespace($length)
- {
-
- $this->pos += $length;
-
- for (; $this->pos < $this->input_len; $this->pos++) {
- $c = $this->input[$this->pos];
-
- if (($c !== "\n") && ($c !== "\r") && ($c !== "\t") && ($c !== ' ')) {
- break;
- }
- }
- }
-
-
- /**
- * @param string $tok
- * @param string|null $msg
- */
- public function expect($tok, $msg = null)
- {
- $result = $this->match(array($tok));
- if (!$result) {
- $this->Error($msg ? "Expected '" . $tok . "' got '" . $this->input[$this->pos] . "'" : $msg);
- } else {
- return $result;
- }
- }
-
- /**
- * @param string $tok
- */
- public function expectChar($tok, $msg = null)
- {
- $result = $this->MatchChar($tok);
- if (!$result) {
- $this->Error($msg ? "Expected '" . $tok . "' got '" . $this->input[$this->pos] . "'" : $msg);
- } else {
- return $result;
- }
- }
-
- //
- // Here in, the parsing rules/functions
- //
- // The basic structure of the syntax tree generated is as follows:
- //
- // Ruleset -> Rule -> Value -> Expression -> Entity
- //
- // Here's some LESS code:
- //
- // .class {
- // color: #fff;
- // border: 1px solid #000;
- // width: @w + 4px;
- // > .child {...}
- // }
- //
- // And here's what the parse tree might look like:
- //
- // Ruleset (Selector '.class', [
- // Rule ("color", Value ([Expression [Color #fff]]))
- // Rule ("border", Value ([Expression [Dimension 1px][Keyword "solid"][Color #000]]))
- // Rule ("width", Value ([Expression [Operation "+" [Variable "@w"][Dimension 4px]]]))
- // Ruleset (Selector [Element '>', '.child'], [...])
- // ])
- //
- // In general, most rules will try to parse a token with the `$()` function, and if the return
- // value is truly, will return a new node, of the relevant type. Sometimes, we need to check
- // first, before parsing, that's when we use `peek()`.
- //
-
- //
- // The `primary` rule is the *entry* and *exit* point of the parser.
- // The rules here can appear at any level of the parse tree.
- //
- // The recursive nature of the grammar is an interplay between the `block`
- // rule, which represents `{ ... }`, the `ruleset` rule, and this `primary` rule,
- // as represented by this simplified grammar:
- //
- // primary > (ruleset | rule)+
- // ruleset > selector+ block
- // block > '{' primary '}'
- //
- // Only at one point is the primary rule not called from the
- // block rule: at the root level.
- //
- private function parsePrimary()
- {
- $root = array();
-
- while (true) {
-
- if ($this->pos >= $this->input_len) {
- break;
- }
-
- $node = $this->parseExtend(true);
- if ($node) {
- $root = array_merge($root, $node);
- continue;
- }
-
- //$node = $this->MatchFuncs( array( 'parseMixinDefinition', 'parseRule', 'parseRuleset', 'parseMixinCall', 'parseComment', 'parseDirective'));
- $node = $this->MatchFuncs(array('parseMixinDefinition', 'parseNameValue', 'parseRule', 'parseRuleset', 'parseMixinCall', 'parseComment', 'parseRulesetCall', 'parseDirective'));
-
- if ($node) {
- $root[] = $node;
- } elseif (!$this->MatchReg('/\\G[\s\n;]+/')) {
- break;
- }
-
- if ($this->PeekChar('}')) {
- break;
- }
- }
-
- return $root;
- }
-
-
-
- // We create a Comment node for CSS comments `/* */`,
- // but keep the LeSS comments `//` silent, by just skipping
- // over them.
- private function parseComment()
- {
-
- if ($this->input[$this->pos] !== '/') {
- return;
- }
-
- if ($this->input[$this->pos + 1] === '/') {
- $match = $this->MatchReg('/\\G\/\/.*/');
- return $this->NewObj4('Less_Tree_Comment', array($match[0], true, $this->pos, $this->env->currentFileInfo));
- }
-
- //$comment = $this->MatchReg('/\\G\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/');
- $comment = $this->MatchReg('/\\G\/\*(?s).*?\*+\/\n?/'); //not the same as less.js to prevent fatal errors
- if ($comment) {
- return $this->NewObj4('Less_Tree_Comment', array($comment[0], false, $this->pos, $this->env->currentFileInfo));
- }
- }
-
- private function parseComments()
- {
- $comments = array();
-
- while ($this->pos < $this->input_len) {
- $comment = $this->parseComment();
- if (!$comment) {
- break;
- }
-
- $comments[] = $comment;
- }
-
- return $comments;
- }
-
-
-
- //
- // A string, which supports escaping " and '
- //
- // "milky way" 'he\'s the one!'
- //
- private function parseEntitiesQuoted()
- {
- $j = $this->pos;
- $e = false;
- $index = $this->pos;
-
- if ($this->input[$this->pos] === '~') {
- $j++;
- $e = true; // Escaped strings
- }
-
- if ($this->input[$j] != '"' && $this->input[$j] !== "'") {
- return;
- }
-
- if ($e) {
- $this->MatchChar('~');
- }
-
- // Fix for #124: match escaped newlines
- //$str = $this->MatchReg('/\\G"((?:[^"\\\\\r\n]|\\\\.)*)"|\'((?:[^\'\\\\\r\n]|\\\\.)*)\'/');
- $str = $this->MatchReg('/\\G"((?:[^"\\\\\r\n]|\\\\.|\\\\\r\n|\\\\[\n\r\f])*)"|\'((?:[^\'\\\\\r\n]|\\\\.|\\\\\r\n|\\\\[\n\r\f])*)\'/');
-
- if ($str) {
- $result = $str[0][0] == '"' ? $str[1] : $str[2];
- return $this->NewObj5('Less_Tree_Quoted', array($str[0], $result, $e, $index, $this->env->currentFileInfo));
- }
- return;
- }
-
-
- //
- // A catch-all word, such as:
- //
- // black border-collapse
- //
- private function parseEntitiesKeyword()
- {
-
- //$k = $this->MatchReg('/\\G[_A-Za-z-][_A-Za-z0-9-]*/');
- $k = $this->MatchReg('/\\G%|\\G[_A-Za-z-][_A-Za-z0-9-]*/');
- if ($k) {
- $k = $k[0];
- $color = $this->fromKeyword($k);
- if ($color) {
- return $color;
- }
- return $this->NewObj1('Less_Tree_Keyword', $k);
- }
- }
-
- // duplicate of Less_Tree_Color::FromKeyword
- private function FromKeyword($keyword)
- {
- $keyword = strtolower($keyword);
-
- if (Less_Colors::hasOwnProperty($keyword)) {
- // detect named color
- return $this->NewObj1('Less_Tree_Color', substr(Less_Colors::color($keyword), 1));
- }
-
- if ($keyword === 'transparent') {
- return $this->NewObj3('Less_Tree_Color', array(array(0, 0, 0), 0, true));
- }
- }
-
- //
- // A function call
- //
- // rgb(255, 0, 255)
- //
- // We also try to catch IE's `alpha()`, but let the `alpha` parser
- // deal with the details.
- //
- // The arguments are parsed with the `entities.arguments` parser.
- //
- private function parseEntitiesCall()
- {
- $index = $this->pos;
-
- if (!preg_match('/\\G([\w-]+|%|progid:[\w\.]+)\(/', $this->input, $name, 0, $this->pos)) {
- return;
- }
- $name = $name[1];
- $nameLC = strtolower($name);
-
- if ($nameLC === 'url') {
- return null;
- }
-
- $this->pos += strlen($name);
-
- if ($nameLC === 'alpha') {
- $alpha_ret = $this->parseAlpha();
- if ($alpha_ret) {
- return $alpha_ret;
- }
- }
-
- $this->MatchChar('('); // Parse the '(' and consume whitespace.
-
- $args = $this->parseEntitiesArguments();
-
- if (!$this->MatchChar(')')) {
- return;
- }
-
- if ($name) {
- return $this->NewObj4('Less_Tree_Call', array($name, $args, $index, $this->env->currentFileInfo));
- }
- }
-
- /**
- * Parse a list of arguments
- *
- * @return array
- */
- private function parseEntitiesArguments()
- {
-
- $args = array();
- while (true) {
- $arg = $this->MatchFuncs(array('parseEntitiesAssignment', 'parseExpression'));
- if (!$arg) {
- break;
- }
-
- $args[] = $arg;
- if (!$this->MatchChar(',')) {
- break;
- }
- }
- return $args;
- }
-
- private function parseEntitiesLiteral()
- {
- return $this->MatchFuncs(array('parseEntitiesDimension', 'parseEntitiesColor', 'parseEntitiesQuoted', 'parseUnicodeDescriptor'));
- }
-
- // Assignments are argument entities for calls.
- // They are present in ie filter properties as shown below.
- //
- // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* )
- //
- private function parseEntitiesAssignment()
- {
-
- $key = $this->MatchReg('/\\G\w+(?=\s?=)/');
- if (!$key) {
- return;
- }
-
- if (!$this->MatchChar('=')) {
- return;
- }
-
- $value = $this->parseEntity();
- if ($value) {
- return $this->NewObj2('Less_Tree_Assignment', array($key[0], $value));
- }
- }
-
- //
- // Parse url() tokens
- //
- // We use a specific rule for urls, because they don't really behave like
- // standard function calls. The difference is that the argument doesn't have
- // to be enclosed within a string, so it can't be parsed as an Expression.
- //
- private function parseEntitiesUrl()
- {
-
-
- if ($this->input[$this->pos] !== 'u' || !$this->matchReg('/\\Gurl\(/')) {
- return;
- }
-
- $value = $this->match(array('parseEntitiesQuoted', 'parseEntitiesVariable', '/\\Gdata\:.*?[^\)]+/', '/\\G(?:(?:\\\\[\(\)\'"])|[^\(\)\'"])+/'));
- if (!$value) {
- $value = '';
- }
-
-
- $this->expectChar(')');
-
-
- if (isset($value->value) || $value instanceof Less_Tree_Variable) {
- return $this->NewObj2('Less_Tree_Url', array($value, $this->env->currentFileInfo));
- }
-
- return $this->NewObj2('Less_Tree_Url', array($this->NewObj1('Less_Tree_Anonymous', $value), $this->env->currentFileInfo));
- }
-
-
- //
- // A Variable entity, such as `@fink`, in
- //
- // width: @fink + 2px
- //
- // We use a different parser for variable definitions,
- // see `parsers.variable`.
- //
- private function parseEntitiesVariable()
- {
- $index = $this->pos;
- if ($this->PeekChar('@') && ($name = $this->MatchReg('/\\G@@?[\w-]+/'))) {
- return $this->NewObj3('Less_Tree_Variable', array($name[0], $index, $this->env->currentFileInfo));
- }
- }
-
-
- // A variable entity useing the protective {} e.g. @{var}
- private function parseEntitiesVariableCurly()
- {
- $index = $this->pos;
-
- if ($this->input_len > ($this->pos + 1) && $this->input[$this->pos] === '@' && ($curly = $this->MatchReg('/\\G@\{([\w-]+)\}/'))) {
- return $this->NewObj3('Less_Tree_Variable', array('@' . $curly[1], $index, $this->env->currentFileInfo));
- }
- }
-
- //
- // A Hexadecimal color
- //
- // #4F3C2F
- //
- // `rgb` and `hsl` colors are parsed through the `entities.call` parser.
- //
- private function parseEntitiesColor()
- {
- if ($this->PeekChar('#') && ($rgb = $this->MatchReg('/\\G#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/'))) {
- return $this->NewObj1('Less_Tree_Color', $rgb[1]);
- }
- }
-
- //
- // A Dimension, that is, a number and a unit
- //
- // 0.5em 95%
- //
- private function parseEntitiesDimension()
- {
-
- $c = @ord($this->input[$this->pos]);
-
- //Is the first char of the dimension 0-9, '.', '+' or '-'
- if (($c > 57 || $c < 43) || $c === 47 || $c == 44) {
- return;
- }
-
- $value = $this->MatchReg('/\\G([+-]?\d*\.?\d+)(%|[a-z]+)?/');
- if ($value) {
-
- if (isset($value[2])) {
- return $this->NewObj2('Less_Tree_Dimension', array($value[1], $value[2]));
- }
- return $this->NewObj1('Less_Tree_Dimension', $value[1]);
- }
- }
-
-
- //
- // A unicode descriptor, as is used in unicode-range
- //
- // U+0?? or U+00A1-00A9
- //
- function parseUnicodeDescriptor()
- {
- $ud = $this->MatchReg('/\\G(U\+[0-9a-fA-F?]+)(\-[0-9a-fA-F?]+)?/');
- if ($ud) {
- return $this->NewObj1('Less_Tree_UnicodeDescriptor', $ud[0]);
- }
- }
-
-
- //
- // JavaScript code to be evaluated
- //
- // `window.location.href`
- //
- private function parseEntitiesJavascript()
- {
- $e = false;
- $j = $this->pos;
- if ($this->input[$j] === '~') {
- $j++;
- $e = true;
- }
- if ($this->input[$j] !== '`') {
- return;
- }
- if ($e) {
- $this->MatchChar('~');
- }
- $str = $this->MatchReg('/\\G`([^`]*)`/');
- if ($str) {
- return $this->NewObj3('Less_Tree_Javascript', array($str[1], $this->pos, $e));
- }
- }
-
-
- //
- // The variable part of a variable definition. Used in the `rule` parser
- //
- // @fink:
- //
- private function parseVariable()
- {
- if ($this->PeekChar('@') && ($name = $this->MatchReg('/\\G(@[\w-]+)\s*:/'))) {
- return $name[1];
- }
- }
-
-
- //
- // The variable part of a variable definition. Used in the `rule` parser
- //
- // @fink();
- //
- private function parseRulesetCall()
- {
-
- if ($this->input[$this->pos] === '@' && ($name = $this->MatchReg('/\\G(@[\w-]+)\s*\(\s*\)\s*;/'))) {
- return $this->NewObj1('Less_Tree_RulesetCall', $name[1]);
- }
- }
-
-
- //
- // extend syntax - used to extend selectors
- //
- function parseExtend($isRule = false)
- {
-
- $index = $this->pos;
- $extendList = array();
-
-
- if (!$this->MatchReg($isRule ? '/\\G&:extend\(/' : '/\\G:extend\(/')) {
- return;
- }
-
- do {
- $option = null;
- $elements = array();
- while (true) {
- $option = $this->MatchReg('/\\G(all)(?=\s*(\)|,))/');
- if ($option) {
- break;
- }
- $e = $this->parseElement();
- if (!$e) {
- break;
- }
- $elements[] = $e;
- }
-
- if ($option) {
- $option = $option[1];
- }
-
- $extendList[] = $this->NewObj3('Less_Tree_Extend', array($this->NewObj1('Less_Tree_Selector', $elements), $option, $index));
-
- } while ($this->MatchChar(","));
-
- $this->expect('/\\G\)/');
-
- if ($isRule) {
- $this->expect('/\\G;/');
- }
-
- return $extendList;
- }
-
-
- //
- // A Mixin call, with an optional argument list
- //
- // #mixins > .square(#fff);
- // .rounded(4px, black);
- // .button;
- //
- // The `while` loop is there because mixins can be
- // namespaced, but we only support the child and descendant
- // selector for now.
- //
- private function parseMixinCall()
- {
-
- $char = $this->input[$this->pos];
- if ($char !== '.' && $char !== '#') {
- return;
- }
-
- $index = $this->pos;
- $this->save(); // stop us absorbing part of an invalid selector
-
- $elements = $this->parseMixinCallElements();
-
- if ($elements) {
-
- if ($this->MatchChar('(')) {
- $returned = $this->parseMixinArgs(true);
- $args = $returned['args'];
- $this->expectChar(')');
- } else {
- $args = array();
- }
-
- $important = $this->parseImportant();
-
- if ($this->parseEnd()) {
- $this->forget();
- return $this->NewObj5('Less_Tree_Mixin_Call', array($elements, $args, $index, $this->env->currentFileInfo, $important));
- }
- }
-
- $this->restore();
- }
-
-
- private function parseMixinCallElements()
- {
- $elements = array();
- $c = null;
-
- while (true) {
- $elemIndex = $this->pos;
- $e = $this->MatchReg('/\\G[#.](?:[\w-]|\\\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/');
- if (!$e) {
- break;
- }
- $elements[] = $this->NewObj4('Less_Tree_Element', array($c, $e[0], $elemIndex, $this->env->currentFileInfo));
- $c = $this->MatchChar('>');
- }
-
- return $elements;
- }
-
-
- /**
- * @param boolean $isCall
- */
- private function parseMixinArgs($isCall)
- {
- $expressions = array();
- $argsSemiColon = array();
- $isSemiColonSeperated = null;
- $argsComma = array();
- $expressionContainsNamed = null;
- $name = null;
- $returner = array('args' => array(), 'variadic' => false);
-
- $this->save();
-
- while (true) {
- if ($isCall) {
- $arg = $this->MatchFuncs(array('parseDetachedRuleset', 'parseExpression'));
- } else {
- $this->parseComments();
- if ($this->input[$this->pos] === '.' && $this->MatchReg('/\\G\.{3}/')) {
- $returner['variadic'] = true;
- if ($this->MatchChar(";") && !$isSemiColonSeperated) {
- $isSemiColonSeperated = true;
- }
-
- if ($isSemiColonSeperated) {
- $argsSemiColon[] = array('variadic' => true);
- } else {
- $argsComma[] = array('variadic' => true);
- }
- break;
- }
- $arg = $this->MatchFuncs(array('parseEntitiesVariable', 'parseEntitiesLiteral', 'parseEntitiesKeyword'));
- }
-
- if (!$arg) {
- break;
- }
-
-
- $nameLoop = null;
- if ($arg instanceof Less_Tree_Expression) {
- $arg->throwAwayComments();
- }
- $value = $arg;
- $val = null;
-
- if ($isCall) {
- // Variable
- if (property_exists($arg, 'value') && count($arg->value) == 1) {
- $val = $arg->value[0];
- }
- } else {
- $val = $arg;
- }
-
-
- if ($val instanceof Less_Tree_Variable) {
-
- if ($this->MatchChar(':')) {
- if ($expressions) {
- if ($isSemiColonSeperated) {
- $this->Error('Cannot mix ; and , as delimiter types');
- }
- $expressionContainsNamed = true;
- }
-
- // we do not support setting a ruleset as a default variable - it doesn't make sense
- // However if we do want to add it, there is nothing blocking it, just don't error
- // and remove isCall dependency below
- $value = null;
- if ($isCall) {
- $value = $this->parseDetachedRuleset();
- }
- if (!$value) {
- $value = $this->parseExpression();
- }
-
- if (!$value) {
- if ($isCall) {
- $this->Error('could not understand value for named argument');
- } else {
- $this->restore();
- $returner['args'] = array();
- return $returner;
- }
- }
-
- $nameLoop = ($name = $val->name);
- } elseif (!$isCall && $this->MatchReg('/\\G\.{3}/')) {
- $returner['variadic'] = true;
- if ($this->MatchChar(";") && !$isSemiColonSeperated) {
- $isSemiColonSeperated = true;
- }
- if ($isSemiColonSeperated) {
- $argsSemiColon[] = array('name' => $arg->name, 'variadic' => true);
- } else {
- $argsComma[] = array('name' => $arg->name, 'variadic' => true);
- }
- break;
- } elseif (!$isCall) {
- $name = $nameLoop = $val->name;
- $value = null;
- }
- }
-
- if ($value) {
- $expressions[] = $value;
- }
-
- $argsComma[] = array('name' => $nameLoop, 'value' => $value);
-
- if ($this->MatchChar(',')) {
- continue;
- }
-
- if ($this->MatchChar(';') || $isSemiColonSeperated) {
-
- if ($expressionContainsNamed) {
- $this->Error('Cannot mix ; and , as delimiter types');
- }
-
- $isSemiColonSeperated = true;
-
- if (count($expressions) > 1) {
- $value = $this->NewObj1('Less_Tree_Value', $expressions);
- }
- $argsSemiColon[] = array('name' => $name, 'value' => $value);
-
- $name = null;
- $expressions = array();
- $expressionContainsNamed = false;
- }
- }
-
- $this->forget();
- $returner['args'] = ($isSemiColonSeperated ? $argsSemiColon : $argsComma);
- return $returner;
- }
-
-
-
- //
- // A Mixin definition, with a list of parameters
- //
- // .rounded (@radius: 2px, @color) {
- // ...
- // }
- //
- // Until we have a finer grained state-machine, we have to
- // do a look-ahead, to make sure we don't have a mixin call.
- // See the `rule` function for more information.
- //
- // We start by matching `.rounded (`, and then proceed on to
- // the argument list, which has optional default values.
- // We store the parameters in `params`, with a `value` key,
- // if there is a value, such as in the case of `@radius`.
- //
- // Once we've got our params list, and a closing `)`, we parse
- // the `{...}` block.
- //
- private function parseMixinDefinition()
- {
- $cond = null;
-
- $char = $this->input[$this->pos];
- if (($char !== '.' && $char !== '#') || ($char === '{' && $this->PeekReg('/\\G[^{]*\}/'))) {
- return;
- }
-
- $this->save();
-
- $match = $this->MatchReg('/\\G([#.](?:[\w-]|\\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/');
- if ($match) {
- $name = $match[1];
-
- $argInfo = $this->parseMixinArgs(false);
- $params = $argInfo['args'];
- $variadic = $argInfo['variadic'];
-
-
- // .mixincall("@{a}");
- // looks a bit like a mixin definition..
- // also
- // .mixincall(@a: {rule: set;});
- // so we have to be nice and restore
- if (!$this->MatchChar(')')) {
- $this->furthest = $this->pos;
- $this->restore();
- return;
- }
-
-
- $this->parseComments();
-
- if ($this->MatchReg('/\\Gwhen/')) { // Guard
- $cond = $this->expect('parseConditions', 'Expected conditions');
- }
-
- $ruleset = $this->parseBlock();
-
- if (is_array($ruleset)) {
- $this->forget();
- return $this->NewObj5('Less_Tree_Mixin_Definition', array($name, $params, $ruleset, $cond, $variadic));
- }
-
- $this->restore();
- } else {
- $this->forget();
- }
- }
-
- //
- // Entities are the smallest recognized token,
- // and can be found inside a rule's value.
- //
- private function parseEntity()
- {
-
- return $this->MatchFuncs(array('parseEntitiesLiteral', 'parseEntitiesVariable', 'parseEntitiesUrl', 'parseEntitiesCall', 'parseEntitiesKeyword', 'parseEntitiesJavascript', 'parseComment'));
- }
-
- //
- // A Rule terminator. Note that we use `peek()` to check for '}',
- // because the `block` rule will be expecting it, but we still need to make sure
- // it's there, if ';' was ommitted.
- //
- private function parseEnd()
- {
- return $this->MatchChar(';') || $this->PeekChar('}');
- }
-
- //
- // IE's alpha function
- //
- // alpha(opacity=88)
- //
- private function parseAlpha()
- {
-
- if (!$this->MatchReg('/\\G\(opacity=/i')) {
- return;
- }
-
- $value = $this->MatchReg('/\\G[0-9]+/');
- if ($value) {
- $value = $value[0];
- } else {
- $value = $this->parseEntitiesVariable();
- if (!$value) {
- return;
- }
- }
-
- $this->expectChar(')');
- return $this->NewObj1('Less_Tree_Alpha', $value);
- }
-
-
- //
- // A Selector Element
- //
- // div
- // + h1
- // #socks
- // input[type="text"]
- //
- // Elements are the building blocks for Selectors,
- // they are made out of a `Combinator` (see combinator rule),
- // and an element name, such as a tag a class, or `*`.
- //
- private function parseElement()
- {
- $c = $this->parseCombinator();
- $index = $this->pos;
-
- $e = $this->match(array('/\\G(?:\d+\.\d+|\d+)%/', '/\\G(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/',
- '#*', '#&', 'parseAttribute', '/\\G\([^()@]+\)/', '/\\G[\.#](?=@)/', 'parseEntitiesVariableCurly'));
-
- if (is_null($e)) {
- $this->save();
- if ($this->MatchChar('(')) {
- if (($v = $this->parseSelector()) && $this->MatchChar(')')) {
- $e = $this->NewObj1('Less_Tree_Paren', $v);
- $this->forget();
- } else {
- $this->restore();
- }
- } else {
- $this->forget();
- }
- }
-
- if (!is_null($e)) {
- return $this->NewObj4('Less_Tree_Element', array($c, $e, $index, $this->env->currentFileInfo));
- }
- }
-
- //
- // Combinators combine elements together, in a Selector.
- //
- // Because our parser isn't white-space sensitive, special care
- // has to be taken, when parsing the descendant combinator, ` `,
- // as it's an empty space. We have to check the previous character
- // in the input, to see if it's a ` ` character.
- //
- private function parseCombinator()
- {
- if ($this->pos < $this->input_len) {
- $c = $this->input[$this->pos];
- if ($c === '>' || $c === '+' || $c === '~' || $c === '|' || $c === '^') {
-
- $this->pos++;
- if ($this->input[$this->pos] === '^') {
- $c = '^^';
- $this->pos++;
- }
-
- $this->skipWhitespace(0);
-
- return $c;
- }
-
- if ($this->pos > 0 && $this->isWhitespace(-1)) {
- return ' ';
- }
- }
- }
-
- //
- // A CSS selector (see selector below)
- // with less extensions e.g. the ability to extend and guard
- //
- private function parseLessSelector()
- {
- return $this->parseSelector(true);
- }
-
- //
- // A CSS Selector
- //
- // .class > div + h1
- // li a:hover
- //
- // Selectors are made out of one or more Elements, see above.
- //
- private function parseSelector($isLess = false)
- {
- $elements = array();
- $extendList = array();
- $condition = null;
- $when = false;
- $extend = false;
- $e = null;
- $c = null;
- $index = $this->pos;
-
- while (($isLess && ($extend = $this->parseExtend())) || ($isLess && ($when = $this->MatchReg('/\\Gwhen/'))) || ($e = $this->parseElement())) {
- if ($when) {
- $condition = $this->expect('parseConditions', 'expected condition');
- } elseif ($condition) {
- //error("CSS guard can only be used at the end of selector");
- } elseif ($extend) {
- $extendList = array_merge($extendList, $extend);
- } else {
- //if( count($extendList) ){
- //error("Extend can only be used at the end of selector");
- //}
- if ($this->pos < $this->input_len) {
- $c = $this->input[$this->pos];
- }
- $elements[] = $e;
- $e = null;
- }
-
- if ($c === '{' || $c === '}' || $c === ';' || $c === ',' || $c === ')') {
- break;
- }
- }
-
- if ($elements) {
- return $this->NewObj5('Less_Tree_Selector', array($elements, $extendList, $condition, $index, $this->env->currentFileInfo));
- }
- if ($extendList) {
- $this->Error('Extend must be used to extend a selector, it cannot be used on its own');
- }
- }
-
- private function parseTag()
- {
- return ($tag = $this->MatchReg('/\\G[A-Za-z][A-Za-z-]*[0-9]?/')) ? $tag : $this->MatchChar('*');
- }
-
- private function parseAttribute()
- {
-
- $val = null;
-
- if (!$this->MatchChar('[')) {
- return;
- }
-
- $key = $this->parseEntitiesVariableCurly();
- if (!$key) {
- $key = $this->expect('/\\G(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\\\.)+/');
- }
-
- $op = $this->MatchReg('/\\G[|~*$^]?=/');
- if ($op) {
- $val = $this->match(array('parseEntitiesQuoted', '/\\G[0-9]+%/', '/\\G[\w-]+/', 'parseEntitiesVariableCurly'));
- }
-
- $this->expectChar(']');
-
- return $this->NewObj3('Less_Tree_Attribute', array($key, $op[0], $val));
- }
-
- //
- // The `block` rule is used by `ruleset` and `mixin.definition`.
- // It's a wrapper around the `primary` rule, with added `{}`.
- //
- private function parseBlock()
- {
- if ($this->MatchChar('{')) {
- $content = $this->parsePrimary();
- if ($this->MatchChar('}')) {
- return $content;
- }
- }
- }
-
- private function parseBlockRuleset()
- {
- $block = $this->parseBlock();
-
- if ($block) {
- $block = $this->NewObj2('Less_Tree_Ruleset', array(null, $block));
- }
-
- return $block;
- }
-
- private function parseDetachedRuleset()
- {
- $blockRuleset = $this->parseBlockRuleset();
- if ($blockRuleset) {
- return $this->NewObj1('Less_Tree_DetachedRuleset', $blockRuleset);
- }
- }
-
- //
- // div, .class, body > p {...}
- //
- private function parseRuleset()
- {
- $selectors = array();
-
- $this->save();
-
- while (true) {
- $s = $this->parseLessSelector();
- if (!$s) {
- break;
- }
- $selectors[] = $s;
- $this->parseComments();
-
- if ($s->condition && count($selectors) > 1) {
- $this->Error('Guards are only currently allowed on a single selector.');
- }
-
- if (!$this->MatchChar(',')) {
- break;
- }
- if ($s->condition) {
- $this->Error('Guards are only currently allowed on a single selector.');
- }
- $this->parseComments();
- }
-
-
- if ($selectors) {
- $rules = $this->parseBlock();
- if (is_array($rules)) {
- $this->forget();
- return $this->NewObj2('Less_Tree_Ruleset', array($selectors, $rules)); //Less_Environment::$strictImports
- }
- }
-
- // Backtrack
- $this->furthest = $this->pos;
- $this->restore();
- }
-
- /**
- * Custom less.php parse function for finding simple name-value css pairs
- * ex: width:100px;
- *
- */
- private function parseNameValue()
- {
-
- $index = $this->pos;
- $this->save();
-
-
- //$match = $this->MatchReg('/\\G([a-zA-Z\-]+)\s*:\s*((?:\'")?[a-zA-Z0-9\-% \.,!]+?(?:\'")?)\s*([;}])/');
- $match = $this->MatchReg('/\\G([a-zA-Z\-]+)\s*:\s*([\'"]?[#a-zA-Z0-9\-%\.,]+?[\'"]?) *(! *important)?\s*([;}])/');
- if ($match) {
-
- if ($match[4] == '}') {
- $this->pos = $index + strlen($match[0]) - 1;
- }
-
- if ($match[3]) {
- $match[2] .= ' !important';
- }
-
- return $this->NewObj4('Less_Tree_NameValue', array($match[1], $match[2], $index, $this->env->currentFileInfo));
- }
-
- $this->restore();
- }
-
-
- private function parseRule($tryAnonymous = null)
- {
-
- $merge = false;
- $startOfRule = $this->pos;
-
- $c = $this->input[$this->pos];
- if ($c === '.' || $c === '#' || $c === '&') {
- return;
- }
-
- $this->save();
- $name = $this->MatchFuncs(array('parseVariable', 'parseRuleProperty'));
-
- if ($name) {
-
- $isVariable = is_string($name);
-
- $value = null;
- if ($isVariable) {
- $value = $this->parseDetachedRuleset();
- }
-
- $important = null;
- if (!$value) {
-
- // prefer to try to parse first if its a variable or we are compressing
- // but always fallback on the other one
- //if( !$tryAnonymous && is_string($name) && $name[0] === '@' ){
- if (!$tryAnonymous && (Less_Parser::$options['compress'] || $isVariable)) {
- $value = $this->MatchFuncs(array('parseValue', 'parseAnonymousValue'));
- } else {
- $value = $this->MatchFuncs(array('parseAnonymousValue', 'parseValue'));
- }
-
- $important = $this->parseImportant();
-
- // a name returned by this.ruleProperty() is always an array of the form:
- // [string-1, ..., string-n, ""] or [string-1, ..., string-n, "+"]
- // where each item is a tree.Keyword or tree.Variable
- if (!$isVariable && is_array($name)) {
- $nm = array_pop($name);
- if ($nm->value) {
- $merge = $nm->value;
- }
- }
- }
-
-
- if ($value && $this->parseEnd()) {
- $this->forget();
- return $this->NewObj6('Less_Tree_Rule', array($name, $value, $important, $merge, $startOfRule, $this->env->currentFileInfo));
- } else {
- $this->furthest = $this->pos;
- $this->restore();
- if ($value && !$tryAnonymous) {
- return $this->parseRule(true);
- }
- }
- } else {
- $this->forget();
- }
- }
-
- function parseAnonymousValue()
- {
-
- if (preg_match('/\\G([^@+\/\'"*`(;{}-]*);/', $this->input, $match, 0, $this->pos)) {
- $this->pos += strlen($match[1]);
- return $this->NewObj1('Less_Tree_Anonymous', $match[1]);
- }
- }
-
- //
- // An @import directive
- //
- // @import "lib";
- //
- // Depending on our environment, importing is done differently:
- // In the browser, it's an XHR request, in Node, it would be a
- // file-system operation. The function used for importing is
- // stored in `import`, which we pass to the Import constructor.
- //
- private function parseImport()
- {
-
- $this->save();
-
- $dir = $this->MatchReg('/\\G@import?\s+/');
-
- if ($dir) {
- $options = $this->parseImportOptions();
- $path = $this->MatchFuncs(array('parseEntitiesQuoted', 'parseEntitiesUrl'));
-
- if ($path) {
- $features = $this->parseMediaFeatures();
- if ($this->MatchChar(';')) {
- if ($features) {
- $features = $this->NewObj1('Less_Tree_Value', $features);
- }
-
- $this->forget();
- return $this->NewObj5('Less_Tree_Import', array($path, $features, $options, $this->pos, $this->env->currentFileInfo));
- }
- }
- }
-
- $this->restore();
- }
-
- private function parseImportOptions()
- {
-
- $options = array();
-
- // list of options, surrounded by parens
- if (!$this->MatchChar('(')) {
- return $options;
- }
- do {
- $optionName = $this->parseImportOption();
- if ($optionName) {
- $value = true;
- switch ($optionName) {
- case "css":
- $optionName = "less";
- $value = false;
- break;
- case "once":
- $optionName = "multiple";
- $value = false;
- break;
- }
- $options[$optionName] = $value;
- if (!$this->MatchChar(',')) {
- break;
- }
- }
- } while ($optionName);
- $this->expectChar(')');
- return $options;
- }
-
- private function parseImportOption()
- {
- $opt = $this->MatchReg('/\\G(less|css|multiple|once|inline|reference)/');
- if ($opt) {
- return $opt[1];
- }
- }
-
- private function parseMediaFeature()
- {
- $nodes = array();
-
- do {
- $e = $this->MatchFuncs(array('parseEntitiesKeyword', 'parseEntitiesVariable'));
- if ($e) {
- $nodes[] = $e;
- } elseif ($this->MatchChar('(')) {
- $p = $this->parseProperty();
- $e = $this->parseValue();
- if ($this->MatchChar(')')) {
- if ($p && $e) {
- $r = $this->NewObj7('Less_Tree_Rule', array($p, $e, null, null, $this->pos, $this->env->currentFileInfo, true));
- $nodes[] = $this->NewObj1('Less_Tree_Paren', $r);
- } elseif ($e) {
- $nodes[] = $this->NewObj1('Less_Tree_Paren', $e);
- } else {
- return null;
- }
- } else
- return null;
- }
- } while ($e);
-
- if ($nodes) {
- return $this->NewObj1('Less_Tree_Expression', $nodes);
- }
- }
-
- private function parseMediaFeatures()
- {
- $features = array();
-
- do {
- $e = $this->parseMediaFeature();
- if ($e) {
- $features[] = $e;
- if (!$this->MatchChar(',')) break;
- } else {
- $e = $this->parseEntitiesVariable();
- if ($e) {
- $features[] = $e;
- if (!$this->MatchChar(',')) break;
- }
- }
- } while ($e);
-
- return $features ? $features : null;
- }
-
- private function parseMedia()
- {
- if ($this->MatchReg('/\\G@media/')) {
- $features = $this->parseMediaFeatures();
- $rules = $this->parseBlock();
-
- if (is_array($rules)) {
- return $this->NewObj4('Less_Tree_Media', array($rules, $features, $this->pos, $this->env->currentFileInfo));
- }
- }
- }
-
-
- //
- // A CSS Directive
- //
- // @charset "utf-8";
- //
- private function parseDirective()
- {
-
- if (!$this->PeekChar('@')) {
- return;
- }
-
- $rules = null;
- $index = $this->pos;
- $hasBlock = true;
- $hasIdentifier = false;
- $hasExpression = false;
- $hasUnknown = false;
-
-
- $value = $this->MatchFuncs(array('parseImport', 'parseMedia'));
- if ($value) {
- return $value;
- }
-
- $this->save();
-
- $name = $this->MatchReg('/\\G@[a-z-]+/');
-
- if (!$name) return;
- $name = $name[0];
-
-
- $nonVendorSpecificName = $name;
- $pos = strpos($name, '-', 2);
- if ($name[1] == '-' && $pos > 0) {
- $nonVendorSpecificName = "@" . substr($name, $pos + 1);
- }
-
-
- switch ($nonVendorSpecificName) {
- /*
- case "@font-face":
- case "@viewport":
- case "@top-left":
- case "@top-left-corner":
- case "@top-center":
- case "@top-right":
- case "@top-right-corner":
- case "@bottom-left":
- case "@bottom-left-corner":
- case "@bottom-center":
- case "@bottom-right":
- case "@bottom-right-corner":
- case "@left-top":
- case "@left-middle":
- case "@left-bottom":
- case "@right-top":
- case "@right-middle":
- case "@right-bottom":
- hasBlock = true;
- break;
- */
- case "@charset":
- $hasIdentifier = true;
- $hasBlock = false;
- break;
- case "@namespace":
- $hasExpression = true;
- $hasBlock = false;
- break;
- case "@keyframes":
- $hasIdentifier = true;
- break;
- case "@host":
- case "@page":
- case "@document":
- case "@supports":
- $hasUnknown = true;
- break;
- }
-
- if ($hasIdentifier) {
- $value = $this->parseEntity();
- if (!$value) {
- $this->error("expected " . $name . " identifier");
- }
- } else if ($hasExpression) {
- $value = $this->parseExpression();
- if (!$value) {
- $this->error("expected " . $name . " expression");
- }
- } else if ($hasUnknown) {
-
- $value = $this->MatchReg('/\\G[^{;]+/');
- if ($value) {
- $value = $this->NewObj1('Less_Tree_Anonymous', trim($value[0]));
- }
- }
-
- if ($hasBlock) {
- $rules = $this->parseBlockRuleset();
- }
-
- if ($rules || (!$hasBlock && $value && $this->MatchChar(';'))) {
- $this->forget();
- return $this->NewObj5('Less_Tree_Directive', array($name, $value, $rules, $index, $this->env->currentFileInfo));
- }
-
- $this->restore();
- }
-
-
- //
- // A Value is a comma-delimited list of Expressions
- //
- // font-family: Baskerville, Georgia, serif;
- //
- // In a Rule, a Value represents everything after the `:`,
- // and before the `;`.
- //
- private function parseValue()
- {
- $expressions = array();
-
- do {
- $e = $this->parseExpression();
- if ($e) {
- $expressions[] = $e;
- if (!$this->MatchChar(',')) {
- break;
- }
- }
- } while ($e);
-
- if ($expressions) {
- return $this->NewObj1('Less_Tree_Value', $expressions);
- }
- }
-
- private function parseImportant()
- {
- if ($this->PeekChar('!') && $this->MatchReg('/\\G! *important/')) {
- return ' !important';
- }
- }
-
- private function parseSub()
- {
-
- if ($this->MatchChar('(')) {
- $a = $this->parseAddition();
- if ($a) {
- $this->expectChar(')');
- return $this->NewObj2('Less_Tree_Expression', array(array($a), true)); //instead of $e->parens = true so the value is cached
- }
- }
- }
-
-
- /**
- * Parses multiplication operation
- *
- * @return Less_Tree_Operation|null
- */
- function parseMultiplication()
- {
-
- $return = $m = $this->parseOperand();
- if ($return) {
- while (true) {
-
- $isSpaced = $this->isWhitespace(-1);
-
- if ($this->PeekReg('/\\G\/[*\/]/')) {
- break;
- }
-
- $op = $this->MatchChar('/');
- if (!$op) {
- $op = $this->MatchChar('*');
- if (!$op) {
- break;
- }
- }
-
- $a = $this->parseOperand();
-
- if (!$a) {
- break;
- }
-
- $m->parensInOp = true;
- $a->parensInOp = true;
- $return = $this->NewObj3('Less_Tree_Operation', array($op, array($return, $a), $isSpaced));
- }
- }
- return $return;
-
- }
-
-
- /**
- * Parses an addition operation
- *
- * @return Less_Tree_Operation|null
- */
- private function parseAddition()
- {
-
- $return = $m = $this->parseMultiplication();
- if ($return) {
- while (true) {
-
- $isSpaced = $this->isWhitespace(-1);
-
- $op = $this->MatchReg('/\\G[-+]\s+/');
- if ($op) {
- $op = $op[0];
- } else {
- if (!$isSpaced) {
- $op = $this->match(array('#+', '#-'));
- }
- if (!$op) {
- break;
- }
- }
-
- $a = $this->parseMultiplication();
- if (!$a) {
- break;
- }
-
- $m->parensInOp = true;
- $a->parensInOp = true;
- $return = $this->NewObj3('Less_Tree_Operation', array($op, array($return, $a), $isSpaced));
- }
- }
-
- return $return;
- }
-
-
- /**
- * Parses the conditions
- *
- * @return Less_Tree_Condition|null
- */
- private function parseConditions()
- {
- $index = $this->pos;
- $return = $a = $this->parseCondition();
- if ($a) {
- while (true) {
- if (!$this->PeekReg('/\\G,\s*(not\s*)?\(/') || !$this->MatchChar(',')) {
- break;
- }
- $b = $this->parseCondition();
- if (!$b) {
- break;
- }
-
- $return = $this->NewObj4('Less_Tree_Condition', array('or', $return, $b, $index));
- }
- return $return;
- }
- }
-
- private function parseCondition()
- {
- $index = $this->pos;
- $negate = false;
- $c = null;
-
- if ($this->MatchReg('/\\Gnot/')) $negate = true;
- $this->expectChar('(');
- $a = $this->MatchFuncs(array('parseAddition', 'parseEntitiesKeyword', 'parseEntitiesQuoted'));
-
- if ($a) {
- $op = $this->MatchReg('/\\G(?:>=|<=|=<|[<=>])/');
- if ($op) {
- $b = $this->MatchFuncs(array('parseAddition', 'parseEntitiesKeyword', 'parseEntitiesQuoted'));
- if ($b) {
- $c = $this->NewObj5('Less_Tree_Condition', array($op[0], $a, $b, $index, $negate));
- } else {
- $this->Error('Unexpected expression');
- }
- } else {
- $k = $this->NewObj1('Less_Tree_Keyword', 'true');
- $c = $this->NewObj5('Less_Tree_Condition', array('=', $a, $k, $index, $negate));
- }
- $this->expectChar(')');
- return $this->MatchReg('/\\Gand/') ? $this->NewObj3('Less_Tree_Condition', array('and', $c, $this->parseCondition())) : $c;
- }
- }
-
- /**
- * An operand is anything that can be part of an operation,
- * such as a Color, or a Variable
- *
- */
- private function parseOperand()
- {
-
- $negate = false;
- $offset = $this->pos + 1;
- if ($offset >= $this->input_len) {
- return;
- }
- $char = $this->input[$offset];
- if ($char === '@' || $char === '(') {
- $negate = $this->MatchChar('-');
- }
-
- $o = $this->MatchFuncs(array('parseSub', 'parseEntitiesDimension', 'parseEntitiesColor', 'parseEntitiesVariable', 'parseEntitiesCall'));
-
- if ($negate) {
- $o->parensInOp = true;
- $o = $this->NewObj1('Less_Tree_Negative', $o);
- }
-
- return $o;
- }
-
-
- /**
- * Expressions either represent mathematical operations,
- * or white-space delimited Entities.
- *
- * 1px solid black
- * @var * 2
- *
- * @return Less_Tree_Expression|null
- */
- private function parseExpression()
- {
- $entities = array();
-
- do {
- $e = $this->MatchFuncs(array('parseAddition', 'parseEntity'));
- if ($e) {
- $entities[] = $e;
- // operations do not allow keyword "/" dimension (e.g. small/20px) so we support that here
- if (!$this->PeekReg('/\\G\/[\/*]/')) {
- $delim = $this->MatchChar('/');
- if ($delim) {
- $entities[] = $this->NewObj1('Less_Tree_Anonymous', $delim);
- }
- }
- }
- } while ($e);
-
- if ($entities) {
- return $this->NewObj1('Less_Tree_Expression', $entities);
- }
- }
-
-
- /**
- * Parse a property
- * eg: 'min-width', 'orientation', etc
- *
- * @return string
- */
- private function parseProperty()
- {
- $name = $this->MatchReg('/\\G(\*?-?[_a-zA-Z0-9-]+)\s*:/');
- if ($name) {
- return $name[1];
- }
- }
-
-
- /**
- * Parse a rule property
- * eg: 'color', 'width', 'height', etc
- *
- * @return string
- */
- private function parseRuleProperty()
- {
- $offset = $this->pos;
- $name = array();
- $index = array();
- $length = 0;
-
-
- $this->rulePropertyMatch('/\\G(\*?)/', $offset, $length, $index, $name);
- while ($this->rulePropertyMatch('/\\G((?:[\w-]+)|(?:@\{[\w-]+\}))/', $offset, $length, $index, $name)) ; // !
-
- if ((count($name) > 1) && $this->rulePropertyMatch('/\\G\s*((?:\+_|\+)?)\s*:/', $offset, $length, $index, $name)) {
- // at last, we have the complete match now. move forward,
- // convert name particles to tree objects and return:
- $this->skipWhitespace($length);
-
- if ($name[0] === '') {
- array_shift($name);
- array_shift($index);
- }
- foreach ($name as $k => $s) {
- if (!$s || $s[0] !== '@') {
- $name[$k] = $this->NewObj1('Less_Tree_Keyword', $s);
- } else {
- $name[$k] = $this->NewObj3('Less_Tree_Variable', array('@' . substr($s, 2, -1), $index[$k], $this->env->currentFileInfo));
- }
- }
- return $name;
- }
-
-
- }
-
- private function rulePropertyMatch($re, &$offset, &$length, &$index, &$name)
- {
- preg_match($re, $this->input, $a, 0, $offset);
- if ($a) {
- $index[] = $this->pos + $length;
- $length += strlen($a[0]);
- $offset += strlen($a[0]);
- $name[] = $a[1];
- return true;
- }
- }
-
- public static function serializeVars($vars)
- {
- $s = '';
-
- foreach ($vars as $name => $value) {
- $s .= (($name[0] === '@') ? '' : '@') . $name . ': ' . $value . ((substr($value, -1) === ';') ? '' : ';');
- }
-
- return $s;
- }
-
-
- /**
- * Some versions of php have trouble with method_exists($a,$b) if $a is not an object
- *
- * @param string $b
- */
- public static function is_method($a, $b)
- {
- return is_object($a) && method_exists($a, $b);
- }
-
-
- /**
- * Round numbers similarly to javascript
- * eg: 1.499999 to 1 instead of 2
- *
- */
- public static function round($i, $precision = 0)
- {
-
- $precision = pow(10, $precision);
- $i = $i * $precision;
-
- $ceil = ceil($i);
- $floor = floor($i);
- if (($ceil - $i) <= ($i - $floor)) {
- return $ceil / $precision;
- } else {
- return $floor / $precision;
- }
- }
-
-
- /**
- * Create Less_Tree_* objects and optionally generate a cache string
- *
- * @return mixed
- */
- public function NewObj0($class)
- {
- $obj = new $class();
- if ($this->CacheEnabled()) {
- $obj->cache_string = ' new ' . $class . '()';
- }
- return $obj;
- }
-
- public function NewObj1($class, $arg)
- {
- $obj = new $class($arg);
- if ($this->CacheEnabled()) {
- $obj->cache_string = ' new ' . $class . '(' . Less_Parser::ArgString($arg) . ')';
- }
- return $obj;
- }
-
- public function NewObj2($class, $args)
- {
- $obj = new $class($args[0], $args[1]);
- if ($this->CacheEnabled()) {
- $this->ObjCache($obj, $class, $args);
- }
- return $obj;
- }
-
- public function NewObj3($class, $args)
- {
- $obj = new $class($args[0], $args[1], $args[2]);
- if ($this->CacheEnabled()) {
- $this->ObjCache($obj, $class, $args);
- }
- return $obj;
- }
-
- public function NewObj4($class, $args)
- {
- $obj = new $class($args[0], $args[1], $args[2], $args[3]);
- if ($this->CacheEnabled()) {
- $this->ObjCache($obj, $class, $args);
- }
- return $obj;
- }
-
- public function NewObj5($class, $args)
- {
- $obj = new $class($args[0], $args[1], $args[2], $args[3], $args[4]);
- if ($this->CacheEnabled()) {
- $this->ObjCache($obj, $class, $args);
- }
- return $obj;
- }
-
- public function NewObj6($class, $args)
- {
- $obj = new $class($args[0], $args[1], $args[2], $args[3], $args[4], $args[5]);
- if ($this->CacheEnabled()) {
- $this->ObjCache($obj, $class, $args);
- }
- return $obj;
- }
-
- public function NewObj7($class, $args)
- {
- $obj = new $class($args[0], $args[1], $args[2], $args[3], $args[4], $args[5], $args[6]);
- if ($this->CacheEnabled()) {
- $this->ObjCache($obj, $class, $args);
- }
- return $obj;
- }
-
- //caching
- public function ObjCache($obj, $class, $args = array())
- {
- $obj->cache_string = ' new ' . $class . '(' . self::ArgCache($args) . ')';
- }
-
- public function ArgCache($args)
- {
- return implode(',', array_map(array('Less_Parser', 'ArgString'), $args));
- }
-
-
- /**
- * Convert an argument to a string for use in the parser cache
- *
- * @return string
- */
- public static function ArgString($arg)
- {
-
- $type = gettype($arg);
-
- if ($type === 'object') {
- $string = $arg->cache_string;
- unset($arg->cache_string);
- return $string;
-
- } elseif ($type === 'array') {
- $string = ' Array(';
- foreach ($arg as $k => $a) {
- $string .= var_export($k, true) . ' => ' . self::ArgString($a) . ',';
- }
- return $string . ')';
- }
-
- return var_export($arg, true);
- }
-
- public function Error($msg)
- {
- throw new Less_Exception_Parser($msg, null, $this->furthest, $this->env->currentFileInfo);
- }
-
- public static function WinPath($path)
- {
- return str_replace('\\', '/', $path);
- }
-
- public function CacheEnabled()
- {
- return (Less_Parser::$options['cache_method'] && (Less_Cache::$cache_dir || (Less_Parser::$options['cache_method'] == 'callback')));
- }
-
-}
-
-
-/**
- * Utility for css colors
- *
- * @package Less
- * @subpackage color
- */
-class Less_Colors
-{
-
- public static $colors = array(
- 'aliceblue' => '#f0f8ff',
- 'antiquewhite' => '#faebd7',
- 'aqua' => '#00ffff',
- 'aquamarine' => '#7fffd4',
- 'azure' => '#f0ffff',
- 'beige' => '#f5f5dc',
- 'bisque' => '#ffe4c4',
- 'black' => '#000000',
- 'blanchedalmond' => '#ffebcd',
- 'blue' => '#0000ff',
- 'blueviolet' => '#8a2be2',
- 'brown' => '#a52a2a',
- 'burlywood' => '#deb887',
- 'cadetblue' => '#5f9ea0',
- 'chartreuse' => '#7fff00',
- 'chocolate' => '#d2691e',
- 'coral' => '#ff7f50',
- 'cornflowerblue' => '#6495ed',
- 'cornsilk' => '#fff8dc',
- 'crimson' => '#dc143c',
- 'cyan' => '#00ffff',
- 'darkblue' => '#00008b',
- 'darkcyan' => '#008b8b',
- 'darkgoldenrod' => '#b8860b',
- 'darkgray' => '#a9a9a9',
- 'darkgrey' => '#a9a9a9',
- 'darkgreen' => '#006400',
- 'darkkhaki' => '#bdb76b',
- 'darkmagenta' => '#8b008b',
- 'darkolivegreen' => '#556b2f',
- 'darkorange' => '#ff8c00',
- 'darkorchid' => '#9932cc',
- 'darkred' => '#8b0000',
- 'darksalmon' => '#e9967a',
- 'darkseagreen' => '#8fbc8f',
- 'darkslateblue' => '#483d8b',
- 'darkslategray' => '#2f4f4f',
- 'darkslategrey' => '#2f4f4f',
- 'darkturquoise' => '#00ced1',
- 'darkviolet' => '#9400d3',
- 'deeppink' => '#ff1493',
- 'deepskyblue' => '#00bfff',
- 'dimgray' => '#696969',
- 'dimgrey' => '#696969',
- 'dodgerblue' => '#1e90ff',
- 'firebrick' => '#b22222',
- 'floralwhite' => '#fffaf0',
- 'forestgreen' => '#228b22',
- 'fuchsia' => '#ff00ff',
- 'gainsboro' => '#dcdcdc',
- 'ghostwhite' => '#f8f8ff',
- 'gold' => '#ffd700',
- 'goldenrod' => '#daa520',
- 'gray' => '#808080',
- 'grey' => '#808080',
- 'green' => '#008000',
- 'greenyellow' => '#adff2f',
- 'honeydew' => '#f0fff0',
- 'hotpink' => '#ff69b4',
- 'indianred' => '#cd5c5c',
- 'indigo' => '#4b0082',
- 'ivory' => '#fffff0',
- 'khaki' => '#f0e68c',
- 'lavender' => '#e6e6fa',
- 'lavenderblush' => '#fff0f5',
- 'lawngreen' => '#7cfc00',
- 'lemonchiffon' => '#fffacd',
- 'lightblue' => '#add8e6',
- 'lightcoral' => '#f08080',
- 'lightcyan' => '#e0ffff',
- 'lightgoldenrodyellow' => '#fafad2',
- 'lightgray' => '#d3d3d3',
- 'lightgrey' => '#d3d3d3',
- 'lightgreen' => '#90ee90',
- 'lightpink' => '#ffb6c1',
- 'lightsalmon' => '#ffa07a',
- 'lightseagreen' => '#20b2aa',
- 'lightskyblue' => '#87cefa',
- 'lightslategray' => '#778899',
- 'lightslategrey' => '#778899',
- 'lightsteelblue' => '#b0c4de',
- 'lightyellow' => '#ffffe0',
- 'lime' => '#00ff00',
- 'limegreen' => '#32cd32',
- 'linen' => '#faf0e6',
- 'magenta' => '#ff00ff',
- 'maroon' => '#800000',
- 'mediumaquamarine' => '#66cdaa',
- 'mediumblue' => '#0000cd',
- 'mediumorchid' => '#ba55d3',
- 'mediumpurple' => '#9370d8',
- 'mediumseagreen' => '#3cb371',
- 'mediumslateblue' => '#7b68ee',
- 'mediumspringgreen' => '#00fa9a',
- 'mediumturquoise' => '#48d1cc',
- 'mediumvioletred' => '#c71585',
- 'midnightblue' => '#191970',
- 'mintcream' => '#f5fffa',
- 'mistyrose' => '#ffe4e1',
- 'moccasin' => '#ffe4b5',
- 'navajowhite' => '#ffdead',
- 'navy' => '#000080',
- 'oldlace' => '#fdf5e6',
- 'olive' => '#808000',
- 'olivedrab' => '#6b8e23',
- 'orange' => '#ffa500',
- 'orangered' => '#ff4500',
- 'orchid' => '#da70d6',
- 'palegoldenrod' => '#eee8aa',
- 'palegreen' => '#98fb98',
- 'paleturquoise' => '#afeeee',
- 'palevioletred' => '#d87093',
- 'papayawhip' => '#ffefd5',
- 'peachpuff' => '#ffdab9',
- 'peru' => '#cd853f',
- 'pink' => '#ffc0cb',
- 'plum' => '#dda0dd',
- 'powderblue' => '#b0e0e6',
- 'purple' => '#800080',
- 'red' => '#ff0000',
- 'rosybrown' => '#bc8f8f',
- 'royalblue' => '#4169e1',
- 'saddlebrown' => '#8b4513',
- 'salmon' => '#fa8072',
- 'sandybrown' => '#f4a460',
- 'seagreen' => '#2e8b57',
- 'seashell' => '#fff5ee',
- 'sienna' => '#a0522d',
- 'silver' => '#c0c0c0',
- 'skyblue' => '#87ceeb',
- 'slateblue' => '#6a5acd',
- 'slategray' => '#708090',
- 'slategrey' => '#708090',
- 'snow' => '#fffafa',
- 'springgreen' => '#00ff7f',
- 'steelblue' => '#4682b4',
- 'tan' => '#d2b48c',
- 'teal' => '#008080',
- 'thistle' => '#d8bfd8',
- 'tomato' => '#ff6347',
- 'turquoise' => '#40e0d0',
- 'violet' => '#ee82ee',
- 'wheat' => '#f5deb3',
- 'white' => '#ffffff',
- 'whitesmoke' => '#f5f5f5',
- 'yellow' => '#ffff00',
- 'yellowgreen' => '#9acd32'
- );
-
- public static function hasOwnProperty($color)
- {
- return isset(self::$colors[$color]);
- }
-
-
- public static function color($color)
- {
- return self::$colors[$color];
- }
-
-}
-
-
-/**
- * Environment
- *
- * @package Less
- * @subpackage environment
- */
-class Less_Environment
-{
-
- //public $paths = array(); // option - unmodified - paths to search for imports on
- //public static $files = array(); // list of files that have been imported, used for import-once
- //public $rootpath; // option - rootpath to append to URL's
- //public static $strictImports = null; // option -
- //public $insecure; // option - whether to allow imports from insecure ssl hosts
- //public $processImports; // option - whether to process imports. if false then imports will not be imported
- //public $javascriptEnabled; // option - whether JavaScript is enabled. if undefined, defaults to true
- //public $useFileCache; // browser only - whether to use the per file session cache
- public $currentFileInfo; // information about the current file - for error reporting and importing and making urls relative etc.
-
- public $importMultiple = false; // whether we are currently importing multiple copies
-
-
- /**
- * @var array
- */
- public $frames = array();
-
- /**
- * @var array
- */
- public $mediaBlocks = array();
-
- /**
- * @var array
- */
- public $mediaPath = array();
-
- public static $parensStack = 0;
-
- public static $tabLevel = 0;
-
- public static $lastRule = false;
-
- public static $_outputMap;
-
- public static $mixin_stack = 0;
-
- /**
- * @var array
- */
- public $functions = array();
-
-
- public function Init()
- {
-
- self::$parensStack = 0;
- self::$tabLevel = 0;
- self::$lastRule = false;
- self::$mixin_stack = 0;
-
- if (Less_Parser::$options['compress']) {
-
- Less_Environment::$_outputMap = array(
- ',' => ',',
- ': ' => ':',
- '' => '',
- ' ' => ' ',
- ':' => ' :',
- '+' => '+',
- '~' => '~',
- '>' => '>',
- '|' => '|',
- '^' => '^',
- '^^' => '^^'
- );
-
- } else {
-
- Less_Environment::$_outputMap = array(
- ',' => ', ',
- ': ' => ': ',
- '' => '',
- ' ' => ' ',
- ':' => ' :',
- '+' => ' + ',
- '~' => ' ~ ',
- '>' => ' > ',
- '|' => '|',
- '^' => ' ^ ',
- '^^' => ' ^^ '
- );
-
- }
- }
-
-
- public function copyEvalEnv($frames = array())
- {
- $new_env = new Less_Environment();
- $new_env->frames = $frames;
- return $new_env;
- }
-
-
- public static function isMathOn()
- {
- return !Less_Parser::$options['strictMath'] || Less_Environment::$parensStack;
- }
-
- public static function isPathRelative($path)
- {
- return !preg_match('/^(?:[a-z-]+:|\/)/', $path);
- }
-
-
- /**
- * Canonicalize a path by resolving references to '/./', '/../'
- * Does not remove leading "../"
- * @param string path or url
- * @return string Canonicalized path
- *
- */
- public static function normalizePath($path)
- {
-
- $segments = explode('/', $path);
- $segments = array_reverse($segments);
-
- $path = array();
- $path_len = 0;
-
- while ($segments) {
- $segment = array_pop($segments);
- switch ($segment) {
-
- case '.':
- break;
-
- case '..':
- if (!$path_len || ($path[$path_len - 1] === '..')) {
- $path[] = $segment;
- $path_len++;
- } else {
- array_pop($path);
- $path_len--;
- }
- break;
-
- default:
- $path[] = $segment;
- $path_len++;
- break;
- }
- }
-
- return implode('/', $path);
- }
-
-
- public function unshiftFrame($frame)
- {
- array_unshift($this->frames, $frame);
- }
-
- public function shiftFrame()
- {
- return array_shift($this->frames);
- }
-
-}
-
-
-/**
- * Builtin functions
- *
- * @package Less
- * @subpackage function
- * @see http://lesscss.org/functions/
- */
-class Less_Functions
-{
-
- public $env;
- public $currentFileInfo;
-
- function __construct($env, $currentFileInfo = null)
- {
- $this->env = $env;
- $this->currentFileInfo = $currentFileInfo;
- }
-
-
- /**
- * @param string $op
- */
- public static function operate($op, $a, $b)
- {
- switch ($op) {
- case '+':
- return $a + $b;
- case '-':
- return $a - $b;
- case '*':
- return $a * $b;
- case '/':
- return $a / $b;
- }
- }
-
- public static function clamp($val, $max = 1)
- {
- return min(max($val, 0), $max);
- }
-
- public static function fround($value)
- {
-
- if ($value === 0) {
- return $value;
- }
-
- if (Less_Parser::$options['numPrecision']) {
- $p = pow(10, Less_Parser::$options['numPrecision']);
- return round($value * $p) / $p;
- }
- return $value;
- }
-
- public static function number($n)
- {
-
- if ($n instanceof Less_Tree_Dimension) {
- return floatval($n->unit->is('%') ? $n->value / 100 : $n->value);
- } else if (is_numeric($n)) {
- return $n;
- } else {
- throw new Less_Exception_Compiler("color functions take numbers as parameters");
- }
- }
-
- public static function scaled($n, $size = 255)
- {
- if ($n instanceof Less_Tree_Dimension && $n->unit->is('%')) {
- return (float)$n->value * $size / 100;
- } else {
- return Less_Functions::number($n);
- }
- }
-
- public function rgb($r, $g, $b)
- {
- return $this->rgba($r, $g, $b, 1.0);
- }
-
- public function rgba($r, $g, $b, $a)
- {
- $rgb = array($r, $g, $b);
- $rgb = array_map(array('Less_Functions', 'scaled'), $rgb);
-
- $a = self::number($a);
- return new Less_Tree_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::clamp(self::number($s));
- $l = self::clamp(self::number($l));
- $a = self::clamp(self::number($a));
-
- $m2 = $l <= 0.5 ? $l * ($s + 1) : $l + $s - $l * $s;
-
- $m1 = $l * 2 - $m2;
-
- return $this->rgba(self::hsla_hue($h + 1 / 3, $m1, $m2) * 255,
- self::hsla_hue($h, $m1, $m2) * 255,
- self::hsla_hue($h - 1 / 3, $m1, $m2) * 255,
- $a);
- }
-
- /**
- * @param double $h
- */
- public function hsla_hue($h, $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;
- }
-
- public function hsv($h, $s, $v)
- {
- return $this->hsva($h, $s, $v, 1.0);
- }
-
- /**
- * @param double $a
- */
- public function hsva($h, $s, $v, $a)
- {
- $h = ((Less_Functions::number($h) % 360) / 360) * 360;
- $s = Less_Functions::number($s);
- $v = Less_Functions::number($v);
- $a = Less_Functions::number($a);
-
- $i = floor(($h / 60) % 6);
- $f = ($h / 60) - $i;
-
- $vs = array($v,
- $v * (1 - $s),
- $v * (1 - $f * $s),
- $v * (1 - (1 - $f) * $s));
-
- $perm = array(array(0, 3, 1),
- array(2, 0, 1),
- array(1, 0, 3),
- array(1, 2, 0),
- array(3, 1, 0),
- array(0, 1, 2));
-
- return $this->rgba($vs[$perm[$i][0]] * 255,
- $vs[$perm[$i][1]] * 255,
- $vs[$perm[$i][2]] * 255,
- $a);
- }
-
- public function hue($color)
- {
- $c = $color->toHSL();
- return new Less_Tree_Dimension(Less_Parser::round($c['h']));
- }
-
- public function saturation($color)
- {
- $c = $color->toHSL();
- return new Less_Tree_Dimension(Less_Parser::round($c['s'] * 100), '%');
- }
-
- public function lightness($color)
- {
- $c = $color->toHSL();
- return new Less_Tree_Dimension(Less_Parser::round($c['l'] * 100), '%');
- }
-
- public function hsvhue($color)
- {
- $hsv = $color->toHSV();
- return new Less_Tree_Dimension(Less_Parser::round($hsv['h']));
- }
-
-
- public function hsvsaturation($color)
- {
- $hsv = $color->toHSV();
- return new Less_Tree_Dimension(Less_Parser::round($hsv['s'] * 100), '%');
- }
-
- public function hsvvalue($color)
- {
- $hsv = $color->toHSV();
- return new Less_Tree_Dimension(Less_Parser::round($hsv['v'] * 100), '%');
- }
-
- public function red($color)
- {
- return new Less_Tree_Dimension($color->rgb[0]);
- }
-
- public function green($color)
- {
- return new Less_Tree_Dimension($color->rgb[1]);
- }
-
- public function blue($color)
- {
- return new Less_Tree_Dimension($color->rgb[2]);
- }
-
- public function alpha($color)
- {
- $c = $color->toHSL();
- return new Less_Tree_Dimension($c['a']);
- }
-
- public function luma($color)
- {
- return new Less_Tree_Dimension(Less_Parser::round($color->luma() * $color->alpha * 100), '%');
- }
-
- public function luminance($color)
- {
- $luminance =
- (0.2126 * $color->rgb[0] / 255)
- + (0.7152 * $color->rgb[1] / 255)
- + (0.0722 * $color->rgb[2] / 255);
-
- return new Less_Tree_Dimension(Less_Parser::round($luminance * $color->alpha * 100), '%');
- }
-
- public function saturate($color, $amount = null)
- {
- // filter: saturate(3.2);
- // should be kept as is, so check for color
- if (!property_exists($color, 'rgb')) {
- return null;
- }
- $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']);
- }
-
- /**
- * @param Less_Tree_Dimension $amount
- */
- 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)
- {
-
- if ($color instanceof Less_Tree_Color) {
- $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']);
- }
-
- Less_Functions::Expected('color', $color);
- }
-
- public function fadein($color, $amount)
- {
- $hsl = $color->toHSL();
- $hsl['a'] += $amount->value / 100;
- $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();
- $hsl['a'] -= $amount->value / 100;
- $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();
-
- $hsl['a'] = $amount->value / 100;
- $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 = fmod($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
- //
-
- /**
- * @param Less_Tree_Color $color1
- */
- public function mix($color1, $color2, $weight = null)
- {
- if (!$weight) {
- $weight = new Less_Tree_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_Tree_Color($rgb, $alpha);
- }
-
- public function greyscale($color)
- {
- return $this->desaturate($color, new Less_Tree_Dimension(100));
- }
-
-
- public function contrast($color, $dark = null, $light = null, $threshold = null)
- {
- // filter: contrast(3.2);
- // should be kept as is, so check for color
- if (!property_exists($color, 'rgb')) {
- return null;
- }
- if (!$light) {
- $light = $this->rgba(255, 255, 255, 1.0);
- }
- if (!$dark) {
- $dark = $this->rgba(0, 0, 0, 1.0);
- }
- //Figure out which is actually light and dark!
- if ($dark->luma() > $light->luma()) {
- $t = $light;
- $light = $dark;
- $dark = $t;
- }
- if (!$threshold) {
- $threshold = 0.43;
- } else {
- $threshold = Less_Functions::number($threshold);
- }
-
- if ($color->luma() < $threshold) {
- return $light;
- } else {
- return $dark;
- }
- }
-
- public function e($str)
- {
- if (is_string($str)) {
- return new Less_Tree_Anonymous($str);
- }
- return new Less_Tree_Anonymous($str instanceof Less_Tree_JavaScript ? $str->expression : $str->value);
- }
-
- public function escape($str)
- {
-
- $revert = array('%21' => '!', '%2A' => '*', '%27' => "'", '%3F' => '?', '%26' => '&', '%2C' => ',', '%2F' => '/', '%40' => '@', '%2B' => '+', '%24' => '$');
-
- return new Less_Tree_Anonymous(strtr(rawurlencode($str->value), $revert));
- }
-
-
- /**
- * todo: This function will need some additional work to make it work the same as less.js
- *
- */
- public function replace($string, $pattern, $replacement, $flags = null)
- {
- $result = $string->value;
-
- $expr = '/' . str_replace('/', '\\/', $pattern->value) . '/';
- if ($flags && $flags->value) {
- $expr .= self::replace_flags($flags->value);
- }
-
- $result = preg_replace($expr, $replacement->value, $result);
-
-
- if (property_exists($string, 'quote')) {
- return new Less_Tree_Quoted($string->quote, $result, $string->escaped);
- }
- return new Less_Tree_Quoted('', $result);
- }
-
- public static function replace_flags($flags)
- {
- $flags = str_split($flags, 1);
- $new_flags = '';
-
- foreach ($flags as $flag) {
- switch ($flag) {
- case 'e':
- case 'g':
- break;
-
- default:
- $new_flags .= $flag;
- break;
- }
- }
-
- return $new_flags;
- }
-
- public function _percent()
- {
- $string = func_get_arg(0);
-
- $args = func_get_args();
- array_shift($args);
- $result = $string->value;
-
- foreach ($args as $arg) {
- if (preg_match('/%[sda]/i', $result, $token)) {
- $token = $token[0];
- $value = stristr($token, 's') ? $arg->value : $arg->toCSS();
- $value = preg_match('/[A-Z]$/', $token) ? urlencode($value) : $value;
- $result = preg_replace('/%[sda]/i', $value, $result, 1);
- }
- }
- $result = str_replace('%%', '%', $result);
-
- return new Less_Tree_Quoted($string->quote, $result, $string->escaped);
- }
-
- public function unit($val, $unit = null)
- {
- if (!($val instanceof Less_Tree_Dimension)) {
- throw new Less_Exception_Compiler('The first argument to unit must be a number' . ($val instanceof Less_Tree_Operation ? '. Have you forgotten parenthesis?' : '.'));
- }
-
- if ($unit) {
- if ($unit instanceof Less_Tree_Keyword) {
- $unit = $unit->value;
- } else {
- $unit = $unit->toCSS();
- }
- } else {
- $unit = "";
- }
- return new Less_Tree_Dimension($val->value, $unit);
- }
-
- public function convert($val, $unit)
- {
- return $val->convertTo($unit->value);
- }
-
- public function round($n, $f = false)
- {
-
- $fraction = 0;
- if ($f !== false) {
- $fraction = $f->value;
- }
-
- return $this->_math('Less_Parser::round', null, $n, $fraction);
- }
-
- public function pi()
- {
- return new Less_Tree_Dimension(M_PI);
- }
-
- public function mod($a, $b)
- {
- return new Less_Tree_Dimension($a->value % $b->value, $a->unit);
- }
-
-
- public function pow($x, $y)
- {
- if (is_numeric($x) && is_numeric($y)) {
- $x = new Less_Tree_Dimension($x);
- $y = new Less_Tree_Dimension($y);
- } elseif (!($x instanceof Less_Tree_Dimension) || !($y instanceof Less_Tree_Dimension)) {
- throw new Less_Exception_Compiler('Arguments must be numbers');
- }
-
- return new Less_Tree_Dimension(pow($x->value, $y->value), $x->unit);
- }
-
- // var mathFunctions = [{name:"ce ...
- public function ceil($n)
- {
- return $this->_math('ceil', null, $n);
- }
-
- public function floor($n)
- {
- return $this->_math('floor', null, $n);
- }
-
- public function sqrt($n)
- {
- return $this->_math('sqrt', null, $n);
- }
-
- public function abs($n)
- {
- return $this->_math('abs', null, $n);
- }
-
- public function tan($n)
- {
- return $this->_math('tan', '', $n);
- }
-
- public function sin($n)
- {
- return $this->_math('sin', '', $n);
- }
-
- public function cos($n)
- {
- return $this->_math('cos', '', $n);
- }
-
- public function atan($n)
- {
- return $this->_math('atan', 'rad', $n);
- }
-
- public function asin($n)
- {
- return $this->_math('asin', 'rad', $n);
- }
-
- public function acos($n)
- {
- return $this->_math('acos', 'rad', $n);
- }
-
- private function _math()
- {
- $args = func_get_args();
- $fn = array_shift($args);
- $unit = array_shift($args);
-
- if ($args[0] instanceof Less_Tree_Dimension) {
-
- if ($unit === null) {
- $unit = $args[0]->unit;
- } else {
- $args[0] = $args[0]->unify();
- }
- $args[0] = (float)$args[0]->value;
- return new Less_Tree_Dimension(call_user_func_array($fn, $args), $unit);
- } else if (is_numeric($args[0])) {
- return call_user_func_array($fn, $args);
- } else {
- throw new Less_Exception_Compiler("math functions take numbers as parameters");
- }
- }
-
- /**
- * @param boolean $isMin
- */
- private function _minmax($isMin, $args)
- {
-
- $arg_count = count($args);
-
- if ($arg_count < 1) {
- throw new Less_Exception_Compiler('one or more arguments required');
- }
-
- $j = null;
- $unitClone = null;
- $unitStatic = null;
-
-
- $order = array(); // elems only contains original argument values.
- $values = array(); // key is the unit.toString() for unified tree.Dimension values,
- // value is the index into the order array.
-
-
- for ($i = 0; $i < $arg_count; $i++) {
- $current = $args[$i];
- if (!($current instanceof Less_Tree_Dimension)) {
- if (is_array($args[$i]->value)) {
- $args[] = $args[$i]->value;
- }
- continue;
- }
-
- if ($current->unit->toString() === '' && !$unitClone) {
- $temp = new Less_Tree_Dimension($current->value, $unitClone);
- $currentUnified = $temp->unify();
- } else {
- $currentUnified = $current->unify();
- }
-
- if ($currentUnified->unit->toString() === "" && !$unitStatic) {
- $unit = $unitStatic;
- } else {
- $unit = $currentUnified->unit->toString();
- }
-
- if ($unit !== '' && !$unitStatic || $unit !== '' && $order[0]->unify()->unit->toString() === "") {
- $unitStatic = $unit;
- }
-
- if ($unit != '' && !$unitClone) {
- $unitClone = $current->unit->toString();
- }
-
- if (isset($values['']) && $unit !== '' && $unit === $unitStatic) {
- $j = $values[''];
- } elseif (isset($values[$unit])) {
- $j = $values[$unit];
- } else {
-
- if ($unitStatic && $unit !== $unitStatic) {
- throw new Less_Exception_Compiler('incompatible types');
- }
- $values[$unit] = count($order);
- $order[] = $current;
- continue;
- }
-
-
- if ($order[$j]->unit->toString() === "" && $unitClone) {
- $temp = new Less_Tree_Dimension($order[$j]->value, $unitClone);
- $referenceUnified = $temp->unifiy();
- } else {
- $referenceUnified = $order[$j]->unify();
- }
- if (($isMin && $currentUnified->value < $referenceUnified->value) || (!$isMin && $currentUnified->value > $referenceUnified->value)) {
- $order[$j] = $current;
- }
- }
-
- if (count($order) == 1) {
- return $order[0];
- }
- $args = array();
- foreach ($order as $a) {
- $args[] = $a->toCSS($this->env);
- }
- return new Less_Tree_Anonymous(($isMin ? 'min(' : 'max(') . implode(Less_Environment::$_outputMap[','], $args) . ')');
- }
-
- public function min()
- {
- $args = func_get_args();
- return $this->_minmax(true, $args);
- }
-
- public function max()
- {
- $args = func_get_args();
- return $this->_minmax(false, $args);
- }
-
- public function getunit($n)
- {
- return new Less_Tree_Anonymous($n->unit);
- }
-
- public function argb($color)
- {
- return new Less_Tree_Anonymous($color->toARGB());
- }
-
- public function percentage($n)
- {
- return new Less_Tree_Dimension($n->value * 100, '%');
- }
-
- public function color($n)
- {
-
- if ($n instanceof Less_Tree_Quoted) {
- $colorCandidate = $n->value;
- $returnColor = Less_Tree_Color::fromKeyword($colorCandidate);
- if ($returnColor) {
- return $returnColor;
- }
- if (preg_match('/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/', $colorCandidate)) {
- return new Less_Tree_Color(substr($colorCandidate, 1));
- }
- throw new Less_Exception_Compiler("argument must be a color keyword or 3/6 digit hex e.g. #FFF");
- } else {
- throw new Less_Exception_Compiler("argument must be a string");
- }
- }
-
-
- public function iscolor($n)
- {
- return $this->_isa($n, 'Less_Tree_Color');
- }
-
- public function isnumber($n)
- {
- return $this->_isa($n, 'Less_Tree_Dimension');
- }
-
- public function isstring($n)
- {
- return $this->_isa($n, 'Less_Tree_Quoted');
- }
-
- public function iskeyword($n)
- {
- return $this->_isa($n, 'Less_Tree_Keyword');
- }
-
- public function isurl($n)
- {
- return $this->_isa($n, 'Less_Tree_Url');
- }
-
- public function ispixel($n)
- {
- return $this->isunit($n, 'px');
- }
-
- public function ispercentage($n)
- {
- return $this->isunit($n, '%');
- }
-
- public function isem($n)
- {
- return $this->isunit($n, 'em');
- }
-
- /**
- * @param string $unit
- */
- public function isunit($n, $unit)
- {
- return ($n instanceof Less_Tree_Dimension) && $n->unit->is((property_exists($unit, 'value') ? $unit->value : $unit)) ? new Less_Tree_Keyword('true') : new Less_Tree_Keyword('false');
- }
-
- /**
- * @param string $type
- */
- private function _isa($n, $type)
- {
- return is_a($n, $type) ? new Less_Tree_Keyword('true') : new Less_Tree_Keyword('false');
- }
-
- public function tint($color, $amount)
- {
- return $this->mix($this->rgb(255, 255, 255), $color, $amount);
- }
-
- public function shade($color, $amount)
- {
- return $this->mix($this->rgb(0, 0, 0), $color, $amount);
- }
-
- public function extract($values, $index)
- {
- $index = (int)$index->value - 1; // (1-based index)
- // handle non-array values as an array of length 1
- // return 'undefined' if index is invalid
- if (property_exists($values, 'value') && is_array($values->value)) {
- if (isset($values->value[$index])) {
- return $values->value[$index];
- }
- return null;
-
- } elseif ((int)$index === 0) {
- return $values;
- }
-
- return null;
- }
-
- public function length($values)
- {
- $n = (property_exists($values, 'value') && is_array($values->value)) ? count($values->value) : 1;
- return new Less_Tree_Dimension($n);
- }
-
- public function datauri($mimetypeNode, $filePathNode = null)
- {
-
- $filePath = ($filePathNode ? $filePathNode->value : null);
- $mimetype = $mimetypeNode->value;
-
- $args = 2;
- if (!$filePath) {
- $filePath = $mimetype;
- $args = 1;
- }
-
- $filePath = str_replace('\\', '/', $filePath);
- if (Less_Environment::isPathRelative($filePath)) {
-
- if (Less_Parser::$options['relativeUrls']) {
- $temp = $this->currentFileInfo['currentDirectory'];
- } else {
- $temp = $this->currentFileInfo['entryPath'];
- }
-
- if (!empty($temp)) {
- $filePath = Less_Environment::normalizePath(rtrim($temp, '/') . '/' . $filePath);
- }
-
- }
-
-
- // detect the mimetype if not given
- if ($args < 2) {
-
- /* incomplete
- $mime = require('mime');
- mimetype = mime.lookup(path);
-
- // use base 64 unless it's an ASCII or UTF-8 format
- var charset = mime.charsets.lookup(mimetype);
- useBase64 = ['US-ASCII', 'UTF-8'].indexOf(charset) < 0;
- if (useBase64) mimetype += ';base64';
- */
-
- $mimetype = Less_Mime::lookup($filePath);
-
- $charset = Less_Mime::charsets_lookup($mimetype);
- $useBase64 = !in_array($charset, array('US-ASCII', 'UTF-8'));
- if ($useBase64) {
- $mimetype .= ';base64';
- }
-
- } else {
- $useBase64 = preg_match('/;base64$/', $mimetype);
- }
-
-
- if (file_exists($filePath)) {
- $buf = @file_get_contents($filePath);
- } else {
- $buf = false;
- }
-
-
- // IE8 cannot handle a data-uri larger than 32KB. If this is exceeded
- // and the --ieCompat flag is enabled, return a normal url() instead.
- $DATA_URI_MAX_KB = 32;
- $fileSizeInKB = round(strlen($buf) / 1024);
- if ($fileSizeInKB >= $DATA_URI_MAX_KB) {
- $url = new Less_Tree_Url(($filePathNode ? $filePathNode : $mimetypeNode), $this->currentFileInfo);
- return $url->compile($this);
- }
-
- if ($buf) {
- $buf = $useBase64 ? base64_encode($buf) : rawurlencode($buf);
- $filePath = '"data:' . $mimetype . ',' . $buf . '"';
- }
-
- return new Less_Tree_Url(new Less_Tree_Anonymous($filePath));
- }
-
- //svg-gradient
- public function svggradient($direction)
- {
-
- $throw_message = 'svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]';
- $arguments = func_get_args();
-
- if (count($arguments) < 3) {
- throw new Less_Exception_Compiler($throw_message);
- }
-
- $stops = array_slice($arguments, 1);
- $gradientType = 'linear';
- $rectangleDimension = 'x="0" y="0" width="1" height="1"';
- $useBase64 = true;
- $directionValue = $direction->toCSS();
-
-
- switch ($directionValue) {
- case "to bottom":
- $gradientDirectionSvg = 'x1="0%" y1="0%" x2="0%" y2="100%"';
- break;
- case "to right":
- $gradientDirectionSvg = 'x1="0%" y1="0%" x2="100%" y2="0%"';
- break;
- case "to bottom right":
- $gradientDirectionSvg = 'x1="0%" y1="0%" x2="100%" y2="100%"';
- break;
- case "to top right":
- $gradientDirectionSvg = 'x1="0%" y1="100%" x2="100%" y2="0%"';
- break;
- case "ellipse":
- case "ellipse at center":
- $gradientType = "radial";
- $gradientDirectionSvg = 'cx="50%" cy="50%" r="75%"';
- $rectangleDimension = 'x="-50" y="-50" width="101" height="101"';
- break;
- default:
- throw new Less_Exception_Compiler("svg-gradient direction must be 'to bottom', 'to right', 'to bottom right', 'to top right' or 'ellipse at center'");
- }
-
- $returner = '' .
- '' .
- '<' . $gradientType . 'Gradient id="gradient" gradientUnits="userSpaceOnUse" ' . $gradientDirectionSvg . '>';
-
- for ($i = 0; $i < count($stops); $i++) {
- if (is_object($stops[$i]) && property_exists($stops[$i], 'value')) {
- $color = $stops[$i]->value[0];
- $position = $stops[$i]->value[1];
- } else {
- $color = $stops[$i];
- $position = null;
- }
-
- if (!($color instanceof Less_Tree_Color) || (!(($i === 0 || $i + 1 === count($stops)) && $position === null) && !($position instanceof Less_Tree_Dimension))) {
- throw new Less_Exception_Compiler($throw_message);
- }
- if ($position) {
- $positionValue = $position->toCSS();
- } elseif ($i === 0) {
- $positionValue = '0%';
- } else {
- $positionValue = '100%';
- }
- $alpha = $color->alpha;
- $returner .= ' ';
- }
-
- $returner .= '' . $gradientType . 'Gradient> ';
-
-
- if ($useBase64) {
- $returner = "'data:image/svg+xml;base64," . base64_encode($returner) . "'";
- } else {
- $returner = "'data:image/svg+xml," . $returner . "'";
- }
-
- return new Less_Tree_URL(new Less_Tree_Anonymous($returner));
- }
-
-
- /**
- * @param string $type
- */
- private static function Expected($type, $arg)
- {
-
- $debug = debug_backtrace();
- array_shift($debug);
- $last = array_shift($debug);
- $last = array_intersect_key($last, array('function' => '', 'class' => '', 'line' => ''));
-
- $message = 'Object of type ' . get_class($arg) . ' passed to darken function. Expecting `' . $type . '`. ' . $arg->toCSS() . '. ' . print_r($last, true);
- throw new Less_Exception_Compiler($message);
-
- }
-
- /**
- * Php version of javascript's `encodeURIComponent` function
- *
- * @param string $string The string to encode
- * @return string The encoded string
- */
- public static function encodeURIComponent($string)
- {
- $revert = array('%21' => '!', '%2A' => '*', '%27' => "'", '%28' => '(', '%29' => ')');
- return strtr(rawurlencode($string), $revert);
- }
-
-
- // Color Blending
- // ref: http://www.w3.org/TR/compositing-1
-
- public function colorBlend($mode, $color1, $color2)
- {
- $ab = $color1->alpha; // backdrop
- $as = $color2->alpha; // source
- $r = array(); // result
-
- $ar = $as + $ab * (1 - $as);
- for ($i = 0; $i < 3; $i++) {
- $cb = $color1->rgb[$i] / 255;
- $cs = $color2->rgb[$i] / 255;
- $cr = call_user_func($mode, $cb, $cs);
- if ($ar) {
- $cr = ($as * $cs + $ab * ($cb - $as * ($cb + $cs - $cr))) / $ar;
- }
- $r[$i] = $cr * 255;
- }
-
- return new Less_Tree_Color($r, $ar);
- }
-
- public function multiply($color1, $color2)
- {
- return $this->colorBlend(array($this, 'colorBlendMultiply'), $color1, $color2);
- }
-
- private function colorBlendMultiply($cb, $cs)
- {
- return $cb * $cs;
- }
-
- public function screen($color1, $color2)
- {
- return $this->colorBlend(array($this, 'colorBlendScreen'), $color1, $color2);
- }
-
- private function colorBlendScreen($cb, $cs)
- {
- return $cb + $cs - $cb * $cs;
- }
-
- public function overlay($color1, $color2)
- {
- return $this->colorBlend(array($this, 'colorBlendOverlay'), $color1, $color2);
- }
-
- private function colorBlendOverlay($cb, $cs)
- {
- $cb *= 2;
- return ($cb <= 1)
- ? $this->colorBlendMultiply($cb, $cs)
- : $this->colorBlendScreen($cb - 1, $cs);
- }
-
- public function softlight($color1, $color2)
- {
- return $this->colorBlend(array($this, 'colorBlendSoftlight'), $color1, $color2);
- }
-
- private function colorBlendSoftlight($cb, $cs)
- {
- $d = 1;
- $e = $cb;
- if ($cs > 0.5) {
- $e = 1;
- $d = ($cb > 0.25) ? sqrt($cb)
- : ((16 * $cb - 12) * $cb + 4) * $cb;
- }
- return $cb - (1 - 2 * $cs) * $e * ($d - $cb);
- }
-
- public function hardlight($color1, $color2)
- {
- return $this->colorBlend(array($this, 'colorBlendHardlight'), $color1, $color2);
- }
-
- private function colorBlendHardlight($cb, $cs)
- {
- return $this->colorBlendOverlay($cs, $cb);
- }
-
- public function difference($color1, $color2)
- {
- return $this->colorBlend(array($this, 'colorBlendDifference'), $color1, $color2);
- }
-
- private function colorBlendDifference($cb, $cs)
- {
- return abs($cb - $cs);
- }
-
- public function exclusion($color1, $color2)
- {
- return $this->colorBlend(array($this, 'colorBlendExclusion'), $color1, $color2);
- }
-
- private function colorBlendExclusion($cb, $cs)
- {
- return $cb + $cs - 2 * $cb * $cs;
- }
-
- public function average($color1, $color2)
- {
- return $this->colorBlend(array($this, 'colorBlendAverage'), $color1, $color2);
- }
-
- // non-w3c functions:
- public function colorBlendAverage($cb, $cs)
- {
- return ($cb + $cs) / 2;
- }
-
- public function negation($color1, $color2)
- {
- return $this->colorBlend(array($this, 'colorBlendNegation'), $color1, $color2);
- }
-
- public function colorBlendNegation($cb, $cs)
- {
- return 1 - abs($cb + $cs - 1);
- }
-
- // ~ End of Color Blending
-
-}
-
-
-/**
- * Mime lookup
- *
- * @package Less
- * @subpackage node
- */
-class Less_Mime
-{
-
- // this map is intentionally incomplete
- // if you want more, install 'mime' dep
- static $_types = array(
- '.htm' => 'text/html',
- '.html' => 'text/html',
- '.gif' => 'image/gif',
- '.jpg' => 'image/jpeg',
- '.jpeg' => 'image/jpeg',
- '.png' => 'image/png',
- '.ttf' => 'application/x-font-ttf',
- '.otf' => 'application/x-font-otf',
- '.eot' => 'application/vnd.ms-fontobject',
- '.woff' => 'application/x-font-woff',
- '.svg' => 'image/svg+xml',
- );
-
- public static function lookup($filepath)
- {
- $parts = explode('.', $filepath);
- $ext = '.' . strtolower(array_pop($parts));
-
- if (!isset(self::$_types[$ext])) {
- return null;
- }
- return self::$_types[$ext];
- }
-
- public static function charsets_lookup($type = null)
- {
- // assumes all text types are UTF-8
- return $type && preg_match('/^text\//', $type) ? 'UTF-8' : '';
- }
-}
-
-
-/**
- * Tree
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree
-{
-
- public $cache_string;
-
- public function toCSS()
- {
- $output = new Less_Output();
- $this->genCSS($output);
- return $output->toString();
- }
-
-
- /**
- * Generate CSS by adding it to the output object
- *
- * @param Less_Output $output The output
- * @return void
- */
- public function genCSS($output)
- {
- }
-
-
- /**
- * @param Less_Tree_Ruleset[] $rules
- */
- public static function outputRuleset($output, $rules)
- {
-
- $ruleCnt = count($rules);
- Less_Environment::$tabLevel++;
-
-
- // Compressed
- if (Less_Parser::$options['compress']) {
- $output->add('{');
- for ($i = 0; $i < $ruleCnt; $i++) {
- $rules[$i]->genCSS($output);
- }
-
- $output->add('}');
- Less_Environment::$tabLevel--;
- return;
- }
-
-
- // Non-compressed
- $tabSetStr = "\n" . str_repeat(' ', Less_Environment::$tabLevel - 1);
- $tabRuleStr = $tabSetStr . ' ';
-
- $output->add(" {");
- for ($i = 0; $i < $ruleCnt; $i++) {
- $output->add($tabRuleStr);
- $rules[$i]->genCSS($output);
- }
- Less_Environment::$tabLevel--;
- $output->add($tabSetStr . '}');
-
- }
-
- public function accept($visitor)
- {
- }
-
-
- public static function ReferencedArray($rules)
- {
- foreach ($rules as $rule) {
- if (method_exists($rule, 'markReferenced')) {
- $rule->markReferenced();
- }
- }
- }
-
-
- /**
- * Requires php 5.3+
- */
- public static function __set_state($args)
- {
-
- $class = get_called_class();
- $obj = new $class(null, null, null, null);
- foreach ($args as $key => $val) {
- $obj->$key = $val;
- }
- return $obj;
- }
-
-}
-
-/**
- * Parser output
- *
- * @package Less
- * @subpackage output
- */
-class Less_Output
-{
-
- /**
- * Output holder
- *
- * @var string
- */
- protected $strs = array();
-
- /**
- * Adds a chunk to the stack
- *
- * @param string $chunk The chunk to output
- * @param Less_FileInfo $fileInfo The file information
- * @param integer $index The index
- * @param mixed $mapLines
- */
- public function add($chunk, $fileInfo = null, $index = 0, $mapLines = null)
- {
- $this->strs[] = $chunk;
- }
-
- /**
- * Is the output empty?
- *
- * @return boolean
- */
- public function isEmpty()
- {
- return count($this->strs) === 0;
- }
-
-
- /**
- * Converts the output to string
- *
- * @return string
- */
- public function toString()
- {
- return implode('', $this->strs);
- }
-
-}
-
-/**
- * Visitor
- *
- * @package Less
- * @subpackage visitor
- */
-class Less_Visitor
-{
-
- protected $methods = array();
- protected $_visitFnCache = array();
-
- public function __construct()
- {
- $this->_visitFnCache = get_class_methods(get_class($this));
- $this->_visitFnCache = array_flip($this->_visitFnCache);
- }
-
- public function visitObj($node)
- {
-
- $funcName = 'visit' . $node->type;
- if (isset($this->_visitFnCache[$funcName])) {
-
- $visitDeeper = true;
- $this->$funcName($node, $visitDeeper);
-
- if ($visitDeeper) {
- $node->accept($this);
- }
-
- $funcName = $funcName . "Out";
- if (isset($this->_visitFnCache[$funcName])) {
- $this->$funcName($node);
- }
-
- } else {
- $node->accept($this);
- }
-
- return $node;
- }
-
- public function visitArray($nodes)
- {
-
- array_map(array($this, 'visitObj'), $nodes);
- return $nodes;
- }
-}
-
-
-/**
- * Replacing Visitor
- *
- * @package Less
- * @subpackage visitor
- */
-class Less_VisitorReplacing extends Less_Visitor
-{
-
- public function visitObj($node)
- {
-
- $funcName = 'visit' . $node->type;
- if (isset($this->_visitFnCache[$funcName])) {
-
- $visitDeeper = true;
- $node = $this->$funcName($node, $visitDeeper);
-
- if ($node) {
- if ($visitDeeper && is_object($node)) {
- $node->accept($this);
- }
-
- $funcName = $funcName . "Out";
- if (isset($this->_visitFnCache[$funcName])) {
- $this->$funcName($node);
- }
- }
-
- } else {
- $node->accept($this);
- }
-
- return $node;
- }
-
- public function visitArray($nodes)
- {
-
- $newNodes = array();
- foreach ($nodes as $node) {
- $evald = $this->visitObj($node);
- if ($evald) {
- if (is_array($evald)) {
- self::flatten($evald, $newNodes);
- } else {
- $newNodes[] = $evald;
- }
- }
- }
- return $newNodes;
- }
-
- public function flatten($arr, &$out)
- {
-
- foreach ($arr as $item) {
- if (!is_array($item)) {
- $out[] = $item;
- continue;
- }
-
- foreach ($item as $nestedItem) {
- if (is_array($nestedItem)) {
- self::flatten($nestedItem, $out);
- } else {
- $out[] = $nestedItem;
- }
- }
- }
-
- return $out;
- }
-
-}
-
-
-/**
- * Configurable
- *
- * @package Less
- * @subpackage Core
- */
-abstract class Less_Configurable
-{
-
- /**
- * Array of options
- *
- * @var array
- */
- protected $options = array();
-
- /**
- * Array of default options
- *
- * @var array
- */
- protected $defaultOptions = array();
-
-
- /**
- * Set options
- *
- * If $options is an object it will be converted into an array by called
- * it's toArray method.
- *
- * @throws Exception
- * @param array|object $options
- *
- */
- public function setOptions($options)
- {
- $options = array_intersect_key($options, $this->defaultOptions);
- $this->options = array_merge($this->defaultOptions, $this->options, $options);
- }
-
-
- /**
- * Get an option value by name
- *
- * If the option is empty or not set a NULL value will be returned.
- *
- * @param string $name
- * @param mixed $default Default value if confiuration of $name is not present
- * @return mixed
- */
- public function getOption($name, $default = null)
- {
- if (isset($this->options[$name])) {
- return $this->options[$name];
- }
- return $default;
- }
-
-
- /**
- * Set an option
- *
- * @param string $name
- * @param mixed $value
- */
- public function setOption($name, $value)
- {
- $this->options[$name] = $value;
- }
-
-}
-
-/**
- * Alpha
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Alpha extends Less_Tree
-{
- public $value;
- public $type = 'Alpha';
-
- public function __construct($val)
- {
- $this->value = $val;
- }
-
- //function accept( $visitor ){
- // $this->value = $visitor->visit( $this->value );
- //}
-
- public function compile($env)
- {
-
- if (is_object($this->value)) {
- $this->value = $this->value->compile($env);
- }
-
- return $this;
- }
-
- /**
- * @see Less_Tree::genCSS
- */
- public function genCSS($output)
- {
-
- $output->add("alpha(opacity=");
-
- if (is_string($this->value)) {
- $output->add($this->value);
- } else {
- $this->value->genCSS($output);
- }
-
- $output->add(')');
- }
-
- public function toCSS()
- {
- return "alpha(opacity=" . (is_string($this->value) ? $this->value : $this->value->toCSS()) . ")";
- }
-
-
-}
-
-/**
- * Anonymous
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Anonymous extends Less_Tree
-{
- public $value;
- public $quote;
- public $index;
- public $mapLines;
- public $currentFileInfo;
- public $type = 'Anonymous';
-
- /**
- * @param integer $index
- * @param boolean $mapLines
- */
- public function __construct($value, $index = null, $currentFileInfo = null, $mapLines = null)
- {
- $this->value = $value;
- $this->index = $index;
- $this->mapLines = $mapLines;
- $this->currentFileInfo = $currentFileInfo;
- }
-
- public function compile()
- {
- return new Less_Tree_Anonymous($this->value, $this->index, $this->currentFileInfo, $this->mapLines);
- }
-
- public function compare($x)
- {
- if (!is_object($x)) {
- return -1;
- }
-
- $left = $this->toCSS();
- $right = $x->toCSS();
-
- if ($left === $right) {
- return 0;
- }
-
- return $left < $right ? -1 : 1;
- }
-
- /**
- * @see Less_Tree::genCSS
- */
- public function genCSS($output)
- {
- $output->add($this->value, $this->currentFileInfo, $this->index, $this->mapLines);
- }
-
- public function toCSS()
- {
- return $this->value;
- }
-
-}
-
-
-/**
- * Assignment
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Assignment extends Less_Tree
-{
-
- public $key;
- public $value;
- public $type = 'Assignment';
-
- public function __construct($key, $val)
- {
- $this->key = $key;
- $this->value = $val;
- }
-
- public function accept($visitor)
- {
- $this->value = $visitor->visitObj($this->value);
- }
-
- public function compile($env)
- {
- return new Less_Tree_Assignment($this->key, $this->value->compile($env));
- }
-
- /**
- * @see Less_Tree::genCSS
- */
- public function genCSS($output)
- {
- $output->add($this->key . '=');
- $this->value->genCSS($output);
- }
-
- public function toCss()
- {
- return $this->key . '=' . $this->value->toCSS();
- }
-}
-
-
-/**
- * Attribute
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Attribute extends Less_Tree
-{
-
- public $key;
- public $op;
- public $value;
- public $type = 'Attribute';
-
- public function __construct($key, $op, $value)
- {
- $this->key = $key;
- $this->op = $op;
- $this->value = $value;
- }
-
- public function compile($env)
- {
-
- $key_obj = is_object($this->key);
- $val_obj = is_object($this->value);
-
- if (!$key_obj && !$val_obj) {
- return $this;
- }
-
- return new Less_Tree_Attribute(
- $key_obj ? $this->key->compile($env) : $this->key,
- $this->op,
- $val_obj ? $this->value->compile($env) : $this->value);
- }
-
- /**
- * @see Less_Tree::genCSS
- */
- public function genCSS($output)
- {
- $output->add($this->toCSS());
- }
-
- public function toCSS()
- {
- $value = $this->key;
-
- if ($this->op) {
- $value .= $this->op;
- $value .= (is_object($this->value) ? $this->value->toCSS() : $this->value);
- }
-
- return '[' . $value . ']';
- }
-}
-
-
-/**
- * Call
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Call extends Less_Tree
-{
- public $value;
-
- protected $name;
- protected $args;
- protected $index;
- protected $currentFileInfo;
- public $type = 'Call';
-
- public function __construct($name, $args, $index, $currentFileInfo = null)
- {
- $this->name = $name;
- $this->args = $args;
- $this->index = $index;
- $this->currentFileInfo = $currentFileInfo;
- }
-
- public function accept($visitor)
- {
- $this->args = $visitor->visitArray($this->args);
- }
-
- //
- // 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 = null)
- {
- $args = array();
- foreach ($this->args as $a) {
- $args[] = $a->compile($env);
- }
-
- $nameLC = strtolower($this->name);
- switch ($nameLC) {
- case '%':
- $nameLC = '_percent';
- break;
-
- case 'get-unit':
- $nameLC = 'getunit';
- break;
-
- case 'data-uri':
- $nameLC = 'datauri';
- break;
-
- case 'svg-gradient':
- $nameLC = 'svggradient';
- break;
- }
-
- $result = null;
- if ($nameLC === 'default') {
- $result = Less_Tree_DefaultFunc::compile();
-
- } else {
-
- if (method_exists('Less_Functions', $nameLC)) { // 1.
- try {
-
- $func = new Less_Functions($env, $this->currentFileInfo);
- $result = call_user_func_array(array($func, $nameLC), $args);
-
- } catch (Exception $e) {
- throw new Less_Exception_Compiler('error evaluating function `' . $this->name . '` ' . $e->getMessage() . ' index: ' . $this->index);
- }
- } elseif (isset($env->functions[$nameLC]) && is_callable($env->functions[$nameLC])) {
- try {
- $result = call_user_func_array($env->functions[$nameLC], $args);
- } catch (Exception $e) {
- throw new Less_Exception_Compiler('error evaluating function `' . $this->name . '` ' . $e->getMessage() . ' index: ' . $this->index);
- }
- }
- }
-
- if ($result !== null) {
- return $result;
- }
-
-
- return new Less_Tree_Call($this->name, $args, $this->index, $this->currentFileInfo);
- }
-
- /**
- * @see Less_Tree::genCSS
- */
- public function genCSS($output)
- {
-
- $output->add($this->name . '(', $this->currentFileInfo, $this->index);
- $args_len = count($this->args);
- for ($i = 0; $i < $args_len; $i++) {
- $this->args[$i]->genCSS($output);
- if ($i + 1 < $args_len) {
- $output->add(', ');
- }
- }
-
- $output->add(')');
- }
-
-
- //public function toCSS(){
- // return $this->compile()->toCSS();
- //}
-
-}
-
-
-/**
- * Color
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Color extends Less_Tree
-{
- public $rgb;
- public $alpha;
- public $isTransparentKeyword;
- public $type = 'Color';
-
- public function __construct($rgb, $a = 1, $isTransparentKeyword = null)
- {
-
- if ($isTransparentKeyword) {
- $this->rgb = $rgb;
- $this->alpha = $a;
- $this->isTransparentKeyword = true;
- return;
- }
-
- $this->rgb = array();
- if (is_array($rgb)) {
- $this->rgb = $rgb;
- } else if (strlen($rgb) == 6) {
- foreach (str_split($rgb, 2) as $c) {
- $this->rgb[] = hexdec($c);
- }
- } else {
- foreach (str_split($rgb, 1) as $c) {
- $this->rgb[] = hexdec($c . $c);
- }
- }
- $this->alpha = is_numeric($a) ? $a : 1;
- }
-
- public function compile()
- {
- return $this;
- }
-
- public function luma()
- {
- $r = $this->rgb[0] / 255;
- $g = $this->rgb[1] / 255;
- $b = $this->rgb[2] / 255;
-
- $r = ($r <= 0.03928) ? $r / 12.92 : pow((($r + 0.055) / 1.055), 2.4);
- $g = ($g <= 0.03928) ? $g / 12.92 : pow((($g + 0.055) / 1.055), 2.4);
- $b = ($b <= 0.03928) ? $b / 12.92 : pow((($b + 0.055) / 1.055), 2.4);
-
- return 0.2126 * $r + 0.7152 * $g + 0.0722 * $b;
- }
-
- /**
- * @see Less_Tree::genCSS
- */
- public function genCSS($output)
- {
- $output->add($this->toCSS());
- }
-
- public function toCSS($doNotCompress = false)
- {
- $compress = Less_Parser::$options['compress'] && !$doNotCompress;
- $alpha = Less_Functions::fround($this->alpha);
-
-
- //
- // 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.
- //
- if ($alpha < 1) {
- if ($alpha === 0 && isset($this->isTransparentKeyword) && $this->isTransparentKeyword) {
- return 'transparent';
- }
-
- $values = array();
- foreach ($this->rgb as $c) {
- $values[] = Less_Functions::clamp(round($c), 255);
- }
- $values[] = $alpha;
-
- $glue = ($compress ? ',' : ', ');
- return "rgba(" . implode($glue, $values) . ")";
- } else {
-
- $color = $this->toRGB();
-
- if ($compress) {
-
- // Convert color to short format
- if ($color[1] === $color[2] && $color[3] === $color[4] && $color[5] === $color[6]) {
- $color = '#' . $color[1] . $color[3] . $color[5];
- }
- }
-
- return $color;
- }
- }
-
- //
- // 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.
- //
-
- /**
- * @param string $op
- */
- public function operate($op, $other)
- {
- $rgb = array();
- $alpha = $this->alpha * (1 - $other->alpha) + $other->alpha;
- for ($c = 0; $c < 3; $c++) {
- $rgb[$c] = Less_Functions::operate($op, $this->rgb[$c], $other->rgb[$c]);
- }
- return new Less_Tree_Color($rgb, $alpha);
- }
-
- public function toRGB()
- {
- return $this->toHex($this->rgb);
- }
-
- 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;
-
- $h = $s = 0;
- if ($max !== $min) {
- $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);
- }
-
- //Adapted from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
- public function toHSV()
- {
- $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);
-
- $v = $max;
-
- $d = $max - $min;
- if ($max === 0) {
- $s = 0;
- } else {
- $s = $d / $max;
- }
-
- $h = 0;
- if ($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, 'v' => $v, 'a' => $a);
- }
-
- public function toARGB()
- {
- $argb = array_merge((array)Less_Parser::round($this->alpha * 255), $this->rgb);
- return $this->toHex($argb);
- }
-
- public function compare($x)
- {
-
- if (!property_exists($x, 'rgb')) {
- return -1;
- }
-
-
- return ($x->rgb[0] === $this->rgb[0] &&
- $x->rgb[1] === $this->rgb[1] &&
- $x->rgb[2] === $this->rgb[2] &&
- $x->alpha === $this->alpha) ? 0 : -1;
- }
-
- public function toHex($v)
- {
-
- $ret = '#';
- foreach ($v as $c) {
- $c = Less_Functions::clamp(Less_Parser::round($c), 255);
- if ($c < 16) {
- $ret .= '0';
- }
- $ret .= dechex($c);
- }
-
- return $ret;
- }
-
-
- /**
- * @param string $keyword
- */
- public static function fromKeyword($keyword)
- {
- $keyword = strtolower($keyword);
-
- if (Less_Colors::hasOwnProperty($keyword)) {
- // detect named color
- return new Less_Tree_Color(substr(Less_Colors::color($keyword), 1));
- }
-
- if ($keyword === 'transparent') {
- return new Less_Tree_Color(array(0, 0, 0), 0, true);
- }
- }
-
-}
-
-
-/**
- * Comment
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Comment extends Less_Tree
-{
-
- public $value;
- public $silent;
- public $isReferenced;
- public $currentFileInfo;
- public $type = 'Comment';
-
- public function __construct($value, $silent, $index = null, $currentFileInfo = null)
- {
- $this->value = $value;
- $this->silent = !!$silent;
- $this->currentFileInfo = $currentFileInfo;
- }
-
- /**
- * @see Less_Tree::genCSS
- */
- public function genCSS($output)
- {
- //if( $this->debugInfo ){
- //$output->add( tree.debugInfo($env, $this), $this->currentFileInfo, $this->index);
- //}
- $output->add(trim($this->value)); //TODO shouldn't need to trim, we shouldn't grab the \n
- }
-
- public function toCSS()
- {
- return Less_Parser::$options['compress'] ? '' : $this->value;
- }
-
- public function isSilent()
- {
- $isReference = ($this->currentFileInfo && isset($this->currentFileInfo['reference']) && (!isset($this->isReferenced) || !$this->isReferenced));
- $isCompressed = Less_Parser::$options['compress'] && !preg_match('/^\/\*!/', $this->value);
- return $this->silent || $isReference || $isCompressed;
- }
-
- public function compile()
- {
- return $this;
- }
-
- public function markReferenced()
- {
- $this->isReferenced = true;
- }
-
-}
-
-
-/**
- * Condition
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Condition extends Less_Tree
-{
-
- public $op;
- public $lvalue;
- public $rvalue;
- public $index;
- public $negate;
- public $type = 'Condition';
-
- public function __construct($op, $l, $r, $i = 0, $negate = false)
- {
- $this->op = trim($op);
- $this->lvalue = $l;
- $this->rvalue = $r;
- $this->index = $i;
- $this->negate = $negate;
- }
-
- public function accept($visitor)
- {
- $this->lvalue = $visitor->visitObj($this->lvalue);
- $this->rvalue = $visitor->visitObj($this->rvalue);
- }
-
- public function compile($env)
- {
- $a = $this->lvalue->compile($env);
- $b = $this->rvalue->compile($env);
-
- switch ($this->op) {
- case 'and':
- $result = $a && $b;
- break;
-
- case 'or':
- $result = $a || $b;
- break;
-
- default:
- if (Less_Parser::is_method($a, 'compare')) {
- $result = $a->compare($b);
- } elseif (Less_Parser::is_method($b, 'compare')) {
- $result = $b->compare($a);
- } else {
- throw new Less_Exception_Compiler('Unable to perform comparison', null, $this->index);
- }
-
- switch ($result) {
- case -1:
- $result = $this->op === '<' || $this->op === '=<' || $this->op === '<=';
- break;
-
- case 0:
- $result = $this->op === '=' || $this->op === '>=' || $this->op === '=<' || $this->op === '<=';
- break;
-
- case 1:
- $result = $this->op === '>' || $this->op === '>=';
- break;
- }
- break;
- }
-
- return $this->negate ? !$result : $result;
- }
-
-}
-
-
-/**
- * DefaultFunc
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_DefaultFunc
-{
-
- static $error_;
- static $value_;
-
- public static function compile()
- {
- if (self::$error_) {
- throw new Exception(self::$error_);
- }
- if (self::$value_ !== null) {
- return self::$value_ ? new Less_Tree_Keyword('true') : new Less_Tree_Keyword('false');
- }
- }
-
- public static function value($v)
- {
- self::$value_ = $v;
- }
-
- public static function error($e)
- {
- self::$error_ = $e;
- }
-
- public static function reset()
- {
- self::$value_ = self::$error_ = null;
- }
-}
-
-/**
- * DetachedRuleset
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_DetachedRuleset extends Less_Tree
-{
-
- public $ruleset;
- public $frames;
- public $type = 'DetachedRuleset';
-
- public function __construct($ruleset, $frames = null)
- {
- $this->ruleset = $ruleset;
- $this->frames = $frames;
- }
-
- public function accept($visitor)
- {
- $this->ruleset = $visitor->visitObj($this->ruleset);
- }
-
- public function compile($env)
- {
- if ($this->frames) {
- $frames = $this->frames;
- } else {
- $frames = $env->frames;
- }
- return new Less_Tree_DetachedRuleset($this->ruleset, $frames);
- }
-
- public function callEval($env)
- {
- if ($this->frames) {
- return $this->ruleset->compile($env->copyEvalEnv(array_merge($this->frames, $env->frames)));
- }
- return $this->ruleset->compile($env);
- }
-}
-
-
-/**
- * Dimension
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Dimension extends Less_Tree
-{
-
- public $value;
- public $unit;
- public $type = 'Dimension';
-
- public function __construct($value, $unit = null)
- {
- $this->value = floatval($value);
-
- if ($unit && ($unit instanceof Less_Tree_Unit)) {
- $this->unit = $unit;
- } elseif ($unit) {
- $this->unit = new Less_Tree_Unit(array($unit));
- } else {
- $this->unit = new Less_Tree_Unit();
- }
- }
-
- public function accept($visitor)
- {
- $this->unit = $visitor->visitObj($this->unit);
- }
-
- public function compile()
- {
- return $this;
- }
-
- public function toColor()
- {
- return new Less_Tree_Color(array($this->value, $this->value, $this->value));
- }
-
- /**
- * @see Less_Tree::genCSS
- */
- public function genCSS($output)
- {
-
- if (Less_Parser::$options['strictUnits'] && !$this->unit->isSingular()) {
- throw new Less_Exception_Compiler("Multiple units in dimension. Correct the units or use the unit function. Bad unit: " . $this->unit->toString());
- }
-
- $value = Less_Functions::fround($this->value);
- $strValue = (string)$value;
-
- if ($value !== 0 && $value < 0.000001 && $value > -0.000001) {
- // would be output 1e-6 etc.
- $strValue = number_format($strValue, 10);
- $strValue = preg_replace('/\.?0+$/', '', $strValue);
- }
-
- if (Less_Parser::$options['compress']) {
- // Zero values doesn't need a unit
- if ($value === 0 && $this->unit->isLength()) {
- $output->add($strValue);
- return $strValue;
- }
-
- // Float values doesn't need a leading zero
- if ($value > 0 && $value < 1 && $strValue[0] === '0') {
- $strValue = substr($strValue, 1);
- }
- }
-
- $output->add($strValue);
- $this->unit->genCSS($output);
- }
-
- 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`.
-
- /**
- * @param string $op
- */
- public function operate($op, $other)
- {
-
- $value = Less_Functions::operate($op, $this->value, $other->value);
- $unit = clone $this->unit;
-
- if ($op === '+' || $op === '-') {
-
- if (!$unit->numerator && !$unit->denominator) {
- $unit->numerator = $other->unit->numerator;
- $unit->denominator = $other->unit->denominator;
- } elseif (!$other->unit->numerator && !$other->unit->denominator) {
- // do nothing
- } else {
- $other = $other->convertTo($this->unit->usedUnits());
-
- if (Less_Parser::$options['strictUnits'] && $other->unit->toString() !== $unit->toCSS()) {
- throw new Less_Exception_Compiler("Incompatible units. Change the units or use the unit function. Bad units: '" . $unit->toString() . "' and " . $other->unit->toString() + "'.");
- }
-
- $value = Less_Functions::operate($op, $this->value, $other->value);
- }
- } elseif ($op === '*') {
- $unit->numerator = array_merge($unit->numerator, $other->unit->numerator);
- $unit->denominator = array_merge($unit->denominator, $other->unit->denominator);
- sort($unit->numerator);
- sort($unit->denominator);
- $unit->cancel();
- } elseif ($op === '/') {
- $unit->numerator = array_merge($unit->numerator, $other->unit->denominator);
- $unit->denominator = array_merge($unit->denominator, $other->unit->numerator);
- sort($unit->numerator);
- sort($unit->denominator);
- $unit->cancel();
- }
- return new Less_Tree_Dimension($value, $unit);
- }
-
- public function compare($other)
- {
- if ($other instanceof Less_Tree_Dimension) {
-
- if ($this->unit->isEmpty() || $other->unit->isEmpty()) {
- $a = $this;
- $b = $other;
- } else {
- $a = $this->unify();
- $b = $other->unify();
- if ($a->unit->compare($b->unit) !== 0) {
- return -1;
- }
- }
- $aValue = $a->value;
- $bValue = $b->value;
-
- if ($bValue > $aValue) {
- return -1;
- } elseif ($bValue < $aValue) {
- return 1;
- } else {
- return 0;
- }
- } else {
- return -1;
- }
- }
-
- public function unify()
- {
- return $this->convertTo(array('length' => 'px', 'duration' => 's', 'angle' => 'rad'));
- }
-
- public function convertTo($conversions)
- {
- $value = $this->value;
- $unit = clone $this->unit;
-
- if (is_string($conversions)) {
- $derivedConversions = array();
- foreach (Less_Tree_UnitConversions::$groups as $i) {
- if (isset(Less_Tree_UnitConversions::${$i}[$conversions])) {
- $derivedConversions = array($i => $conversions);
- }
- }
- $conversions = $derivedConversions;
- }
-
-
- foreach ($conversions as $groupName => $targetUnit) {
- $group = Less_Tree_UnitConversions::${$groupName};
-
- //numerator
- foreach ($unit->numerator as $i => $atomicUnit) {
- $atomicUnit = $unit->numerator[$i];
- if (!isset($group[$atomicUnit])) {
- continue;
- }
-
- $value = $value * ($group[$atomicUnit] / $group[$targetUnit]);
-
- $unit->numerator[$i] = $targetUnit;
- }
-
- //denominator
- foreach ($unit->denominator as $i => $atomicUnit) {
- $atomicUnit = $unit->denominator[$i];
- if (!isset($group[$atomicUnit])) {
- continue;
- }
-
- $value = $value / ($group[$atomicUnit] / $group[$targetUnit]);
-
- $unit->denominator[$i] = $targetUnit;
- }
- }
-
- $unit->cancel();
-
- return new Less_Tree_Dimension($value, $unit);
- }
-}
-
-
-/**
- * Directive
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Directive extends Less_Tree
-{
-
- public $name;
- public $value;
- public $rules;
- public $index;
- public $isReferenced;
- public $currentFileInfo;
- public $debugInfo;
- public $type = 'Directive';
-
- public function __construct($name, $value = null, $rules, $index = null, $currentFileInfo = null, $debugInfo = null)
- {
- $this->name = $name;
- $this->value = $value;
- if ($rules) {
- $this->rules = $rules;
- $this->rules->allowImports = true;
- }
-
- $this->index = $index;
- $this->currentFileInfo = $currentFileInfo;
- $this->debugInfo = $debugInfo;
- }
-
-
- public function accept($visitor)
- {
- if ($this->rules) {
- $this->rules = $visitor->visitObj($this->rules);
- }
- if ($this->value) {
- $this->value = $visitor->visitObj($this->value);
- }
- }
-
-
- /**
- * @see Less_Tree::genCSS
- */
- public function genCSS($output)
- {
- $value = $this->value;
- $rules = $this->rules;
- $output->add($this->name, $this->currentFileInfo, $this->index);
- if ($this->value) {
- $output->add(' ');
- $this->value->genCSS($output);
- }
- if ($this->rules) {
- Less_Tree::outputRuleset($output, array($this->rules));
- } else {
- $output->add(';');
- }
- }
-
- public function compile($env)
- {
-
- $value = $this->value;
- $rules = $this->rules;
- if ($value) {
- $value = $value->compile($env);
- }
-
- if ($rules) {
- $rules = $rules->compile($env);
- $rules->root = true;
- }
-
- return new Less_Tree_Directive($this->name, $value, $rules, $this->index, $this->currentFileInfo, $this->debugInfo);
- }
-
-
- public function variable($name)
- {
- if ($this->rules) {
- return $this->rules->variable($name);
- }
- }
-
- public function find($selector)
- {
- if ($this->rules) {
- return $this->rules->find($selector, $this);
- }
- }
-
- //rulesets: function () { if (this.rules) return tree.Ruleset.prototype.rulesets.apply(this.rules); },
-
- public function markReferenced()
- {
- $this->isReferenced = true;
- if ($this->rules) {
- Less_Tree::ReferencedArray($this->rules->rules);
- }
- }
-
-}
-
-
-/**
- * Element
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Element extends Less_Tree
-{
-
- public $combinator = '';
- public $value = '';
- public $index;
- public $currentFileInfo;
- public $type = 'Element';
-
- public $value_is_object = false;
-
- public function __construct($combinator, $value, $index = null, $currentFileInfo = null)
- {
-
- $this->value = $value;
- $this->value_is_object = is_object($value);
-
- if ($combinator) {
- $this->combinator = $combinator;
- }
-
- $this->index = $index;
- $this->currentFileInfo = $currentFileInfo;
- }
-
- public function accept($visitor)
- {
- if ($this->value_is_object) { //object or string
- $this->value = $visitor->visitObj($this->value);
- }
- }
-
- public function compile($env)
- {
-
- if (Less_Environment::$mixin_stack) {
- return new Less_Tree_Element($this->combinator, ($this->value_is_object ? $this->value->compile($env) : $this->value), $this->index, $this->currentFileInfo);
- }
-
- if ($this->value_is_object) {
- $this->value = $this->value->compile($env);
- }
-
- return $this;
- }
-
- /**
- * @see Less_Tree::genCSS
- */
- public function genCSS($output)
- {
- $output->add($this->toCSS(), $this->currentFileInfo, $this->index);
- }
-
- public function toCSS()
- {
-
- if ($this->value_is_object) {
- $value = $this->value->toCSS();
- } else {
- $value = $this->value;
- }
-
-
- if ($value === '' && $this->combinator && $this->combinator === '&') {
- return '';
- }
-
-
- return Less_Environment::$_outputMap[$this->combinator] . $value;
- }
-
-}
-
-
-/**
- * Expression
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Expression extends Less_Tree
-{
-
- public $value = array();
- public $parens = false;
- public $parensInOp = false;
- public $type = 'Expression';
-
- public function __construct($value, $parens = null)
- {
- $this->value = $value;
- $this->parens = $parens;
- }
-
- public function accept($visitor)
- {
- $this->value = $visitor->visitArray($this->value);
- }
-
- public function compile($env)
- {
-
- $doubleParen = false;
-
- if ($this->parens && !$this->parensInOp) {
- Less_Environment::$parensStack++;
- }
-
- $returnValue = null;
- if ($this->value) {
-
- $count = count($this->value);
-
- if ($count > 1) {
-
- $ret = array();
- foreach ($this->value as $e) {
- $ret[] = $e->compile($env);
- }
- $returnValue = new Less_Tree_Expression($ret);
-
- } else {
-
- if (($this->value[0] instanceof Less_Tree_Expression) && $this->value[0]->parens && !$this->value[0]->parensInOp) {
- $doubleParen = true;
- }
-
- $returnValue = $this->value[0]->compile($env);
- }
-
- } else {
- $returnValue = $this;
- }
-
- if ($this->parens) {
- if (!$this->parensInOp) {
- Less_Environment::$parensStack--;
-
- } elseif (!Less_Environment::isMathOn() && !$doubleParen) {
- $returnValue = new Less_Tree_Paren($returnValue);
-
- }
- }
- return $returnValue;
- }
-
- /**
- * @see Less_Tree::genCSS
- */
- public function genCSS($output)
- {
- $val_len = count($this->value);
- for ($i = 0; $i < $val_len; $i++) {
- $this->value[$i]->genCSS($output);
- if ($i + 1 < $val_len) {
- $output->add(' ');
- }
- }
- }
-
- public function throwAwayComments()
- {
-
- if (is_array($this->value)) {
- $new_value = array();
- foreach ($this->value as $v) {
- if ($v instanceof Less_Tree_Comment) {
- continue;
- }
- $new_value[] = $v;
- }
- $this->value = $new_value;
- }
- }
-}
-
-
-/**
- * Extend
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Extend extends Less_Tree
-{
-
- public $selector;
- public $option;
- public $index;
- public $selfSelectors = array();
- public $allowBefore;
- public $allowAfter;
- public $firstExtendOnThisSelectorPath;
- public $type = 'Extend';
- public $ruleset;
-
-
- public $object_id;
- public $parent_ids = array();
-
- /**
- * @param integer $index
- */
- public function __construct($selector, $option, $index)
- {
- static $i = 0;
- $this->selector = $selector;
- $this->option = $option;
- $this->index = $index;
-
- switch ($option) {
- case "all":
- $this->allowBefore = true;
- $this->allowAfter = true;
- break;
- default:
- $this->allowBefore = false;
- $this->allowAfter = false;
- break;
- }
-
- $this->object_id = $i++;
- $this->parent_ids = array($this->object_id);
- }
-
- public function accept($visitor)
- {
- $this->selector = $visitor->visitObj($this->selector);
- }
-
- public function compile($env)
- {
- Less_Parser::$has_extends = true;
- $this->selector = $this->selector->compile($env);
- return $this;
- //return new Less_Tree_Extend( $this->selector->compile($env), $this->option, $this->index);
- }
-
- public function findSelfSelectors($selectors)
- {
- $selfElements = array();
-
-
- for ($i = 0, $selectors_len = count($selectors); $i < $selectors_len; $i++) {
- $selectorElements = $selectors[$i]->elements;
- // duplicate the logic in genCSS function inside the selector node.
- // future TODO - move both logics into the selector joiner visitor
- if ($i && $selectorElements && $selectorElements[0]->combinator === "") {
- $selectorElements[0]->combinator = ' ';
- }
- $selfElements = array_merge($selfElements, $selectors[$i]->elements);
- }
-
- $this->selfSelectors = array(new Less_Tree_Selector($selfElements));
- }
-
-}
-
-/**
- * CSS @import node
- *
- * The general strategy here is that we don't want to wait
- * for the parsing to be completed, before we start importing
- * the file. That's because in the context of a browser,
- * most of the time will be spent waiting for the server to respond.
- *
- * On creation, we push the import path to our import queue, though
- * `import,push`, we also pass it a callback, which it'll call once
- * the file has been fetched, and parsed.
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Import extends Less_Tree
-{
-
- public $options;
- public $index;
- public $path;
- public $features;
- public $currentFileInfo;
- public $css;
- public $skip;
- public $root;
- public $type = 'Import';
-
- public function __construct($path, $features, $options, $index, $currentFileInfo = null)
- {
- $this->options = $options;
- $this->index = $index;
- $this->path = $path;
- $this->features = $features;
- $this->currentFileInfo = $currentFileInfo;
-
- if (is_array($options)) {
- $this->options += array('inline' => false);
-
- if (isset($this->options['less']) || $this->options['inline']) {
- $this->css = !isset($this->options['less']) || !$this->options['less'] || $this->options['inline'];
- } else {
- $pathValue = $this->getPath();
- if ($pathValue && preg_match('/css([\?;].*)?$/', $pathValue)) {
- $this->css = true;
- }
- }
- }
- }
-
-//
-// The actual import node doesn't return anything, when converted to CSS.
-// The reason is that it's used at the evaluation stage, so that the rules
-// it imports can be treated like any other rules.
-//
-// In `eval`, we make sure all Import nodes get evaluated, recursively, so
-// we end up with a flat structure, which can easily be imported in the parent
-// ruleset.
-//
-
- public function accept($visitor)
- {
-
- if ($this->features) {
- $this->features = $visitor->visitObj($this->features);
- }
- $this->path = $visitor->visitObj($this->path);
-
- if (!$this->options['inline'] && $this->root) {
- $this->root = $visitor->visit($this->root);
- }
- }
-
- /**
- * @see Less_Tree::genCSS
- */
- public function genCSS($output)
- {
- if ($this->css) {
-
- $output->add('@import ', $this->currentFileInfo, $this->index);
-
- $this->path->genCSS($output);
- if ($this->features) {
- $output->add(' ');
- $this->features->genCSS($output);
- }
- $output->add(';');
- }
- }
-
- public function toCSS()
- {
- $features = $this->features ? ' ' . $this->features->toCSS() : '';
-
- if ($this->css) {
- return "@import " . $this->path->toCSS() . $features . ";\n";
- } else {
- return "";
- }
- }
-
- /**
- * @return string
- */
- public function getPath()
- {
- if ($this->path instanceof Less_Tree_Quoted) {
- $path = $this->path->value;
- $path = (isset($this->css) || preg_match('/(\.[a-z]*$)|([\?;].*)$/', $path)) ? $path : $path . '.less';
- } else if ($this->path instanceof Less_Tree_URL) {
- $path = $this->path->value->value;
- } else {
- return null;
- }
-
- //remove query string and fragment
- return preg_replace('/[\?#][^\?]*$/', '', $path);
- }
-
- public function compileForImport($env)
- {
- return new Less_Tree_Import($this->path->compile($env), $this->features, $this->options, $this->index, $this->currentFileInfo);
- }
-
- public function compilePath($env)
- {
- $path = $this->path->compile($env);
- $rootpath = '';
- if ($this->currentFileInfo && $this->currentFileInfo['rootpath']) {
- $rootpath = $this->currentFileInfo['rootpath'];
- }
-
-
- if (!($path instanceof Less_Tree_URL)) {
- if ($rootpath) {
- $pathValue = $path->value;
- // Add the base path if the import is relative
- if ($pathValue && Less_Environment::isPathRelative($pathValue)) {
- $path->value = $this->currentFileInfo['uri_root'] . $pathValue;
- }
- }
- $path->value = Less_Environment::normalizePath($path->value);
- }
-
-
- return $path;
- }
-
- public function compile($env)
- {
-
- $evald = $this->compileForImport($env);
-
- //get path & uri
- $path_and_uri = null;
- if (is_callable(Less_Parser::$options['import_callback'])) {
- $path_and_uri = call_user_func(Less_Parser::$options['import_callback'], $evald);
- }
-
- if (!$path_and_uri) {
- $path_and_uri = $evald->PathAndUri();
- }
-
- if ($path_and_uri) {
- list($full_path, $uri) = $path_and_uri;
- } else {
- $full_path = $uri = $evald->getPath();
- }
-
-
- //import once
- if ($evald->skip($full_path, $env)) {
- return array();
- }
-
- if ($this->options['inline']) {
- //todo needs to reference css file not import
- //$contents = new Less_Tree_Anonymous($this->root, 0, array('filename'=>$this->importedFilename), true );
-
- Less_Parser::AddParsedFile($full_path);
- $contents = new Less_Tree_Anonymous(file_get_contents($full_path), 0, array(), true);
-
- if ($this->features) {
- return new Less_Tree_Media(array($contents), $this->features->value);
- }
-
- return array($contents);
- }
-
-
- // css ?
- if ($evald->css) {
- $features = ($evald->features ? $evald->features->compile($env) : null);
- return new Less_Tree_Import($this->compilePath($env), $features, $this->options, $this->index);
- }
-
-
- return $this->ParseImport($full_path, $uri, $env);
- }
-
-
- /**
- * Using the import directories, get the full absolute path and uri of the import
- *
- * @param Less_Tree_Import $evald
- */
- public function PathAndUri()
- {
-
- $evald_path = $this->getPath();
-
- if ($evald_path) {
-
- $import_dirs = array();
-
- if (Less_Environment::isPathRelative($evald_path)) {
- //if the path is relative, the file should be in the current directory
- $import_dirs[$this->currentFileInfo['currentDirectory']] = $this->currentFileInfo['uri_root'];
-
- } else {
- //otherwise, the file should be relative to the server root
- $import_dirs[$this->currentFileInfo['entryPath']] = $this->currentFileInfo['entryUri'];
-
- //if the user supplied entryPath isn't the actual root
- $import_dirs[$_SERVER['DOCUMENT_ROOT']] = '';
-
- }
-
- // always look in user supplied import directories
- $import_dirs = array_merge($import_dirs, Less_Parser::$options['import_dirs']);
-
-
- foreach ($import_dirs as $rootpath => $rooturi) {
- if (is_callable($rooturi)) {
- list($path, $uri) = call_user_func($rooturi, $evald_path);
- if (is_string($path)) {
- $full_path = $path;
- return array($full_path, $uri);
- }
- } else {
- $path = rtrim($rootpath, '/\\') . '/' . ltrim($evald_path, '/\\');
-
- if (file_exists($path)) {
- $full_path = Less_Environment::normalizePath($path);
- $uri = Less_Environment::normalizePath(dirname($rooturi . $evald_path));
- return array($full_path, $uri);
- }
- }
- }
- }
- }
-
-
- /**
- * Parse the import url and return the rules
- *
- * @return Less_Tree_Media|array
- */
- public function ParseImport($full_path, $uri, $env)
- {
-
- $import_env = clone $env;
- if ((isset($this->options['reference']) && $this->options['reference']) || isset($this->currentFileInfo['reference'])) {
- $import_env->currentFileInfo['reference'] = true;
- }
-
- if ((isset($this->options['multiple']) && $this->options['multiple'])) {
- $import_env->importMultiple = true;
- }
-
- $parser = new Less_Parser($import_env);
- $root = $parser->parseFile($full_path, $uri, true);
-
-
- $ruleset = new Less_Tree_Ruleset(array(), $root->rules);
- $ruleset->evalImports($import_env);
-
- return $this->features ? new Less_Tree_Media($ruleset->rules, $this->features->value) : $ruleset->rules;
- }
-
-
- /**
- * Should the import be skipped?
- *
- * @return boolean|null
- */
- private function Skip($path, $env)
- {
-
- $path = realpath($path);
-
- if ($path && Less_Parser::FileParsed($path)) {
-
- if (isset($this->currentFileInfo['reference'])) {
- return true;
- }
-
- return !isset($this->options['multiple']) && !$env->importMultiple;
- }
-
- }
-}
-
-
-/**
- * Javascript
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Javascript extends Less_Tree
-{
-
- public $type = 'Javascript';
- public $escaped;
- public $expression;
- public $index;
-
- /**
- * @param boolean $index
- * @param boolean $escaped
- */
- public function __construct($string, $index, $escaped)
- {
- $this->escaped = $escaped;
- $this->expression = $string;
- $this->index = $index;
- }
-
- public function compile()
- {
- return new Less_Tree_Anonymous('/* Sorry, can not do JavaScript evaluation in PHP... :( */');
- }
-
-}
-
-
-/**
- * Keyword
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Keyword extends Less_Tree
-{
-
- public $value;
- public $type = 'Keyword';
-
- /**
- * @param string $value
- */
- public function __construct($value)
- {
- $this->value = $value;
- }
-
- public function compile()
- {
- return $this;
- }
-
- /**
- * @see Less_Tree::genCSS
- */
- public function genCSS($output)
- {
-
- if ($this->value === '%') {
- throw new Less_Exception_Compiler("Invalid % without number");
- }
-
- $output->add($this->value);
- }
-
- public function compare($other)
- {
- if ($other instanceof Less_Tree_Keyword) {
- return $other->value === $this->value ? 0 : 1;
- } else {
- return -1;
- }
- }
-}
-
-
-/**
- * Media
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Media extends Less_Tree
-{
-
- public $features;
- public $rules;
- public $index;
- public $currentFileInfo;
- public $isReferenced;
- public $type = 'Media';
-
- public function __construct($value = array(), $features = array(), $index = null, $currentFileInfo = null)
- {
-
- $this->index = $index;
- $this->currentFileInfo = $currentFileInfo;
-
- $selectors = $this->emptySelectors();
-
- $this->features = new Less_Tree_Value($features);
-
- $this->rules = array(new Less_Tree_Ruleset($selectors, $value));
- $this->rules[0]->allowImports = true;
- }
-
- public function accept($visitor)
- {
- $this->features = $visitor->visitObj($this->features);
- $this->rules = $visitor->visitArray($this->rules);
- }
-
- /**
- * @see Less_Tree::genCSS
- */
- public function genCSS($output)
- {
-
- $output->add('@media ', $this->currentFileInfo, $this->index);
- $this->features->genCSS($output);
- Less_Tree::outputRuleset($output, $this->rules);
-
- }
-
- public function compile($env)
- {
-
- $media = new Less_Tree_Media(array(), array(), $this->index, $this->currentFileInfo);
-
- $strictMathBypass = false;
- if (Less_Parser::$options['strictMath'] === false) {
- $strictMathBypass = true;
- Less_Parser::$options['strictMath'] = true;
- }
-
- $media->features = $this->features->compile($env);
-
- if ($strictMathBypass) {
- Less_Parser::$options['strictMath'] = false;
- }
-
- $env->mediaPath[] = $media;
- $env->mediaBlocks[] = $media;
-
- array_unshift($env->frames, $this->rules[0]);
- $media->rules = array($this->rules[0]->compile($env));
- array_shift($env->frames);
-
- array_pop($env->mediaPath);
-
- return !$env->mediaPath ? $media->compileTop($env) : $media->compileNested($env);
- }
-
- public function variable($name)
- {
- return $this->rules[0]->variable($name);
- }
-
- public function find($selector)
- {
- return $this->rules[0]->find($selector, $this);
- }
-
- public function emptySelectors()
- {
- $el = new Less_Tree_Element('', '&', $this->index, $this->currentFileInfo);
- $sels = array(new Less_Tree_Selector(array($el), array(), null, $this->index, $this->currentFileInfo));
- $sels[0]->mediaEmpty = true;
- return $sels;
- }
-
- public function markReferenced()
- {
- $this->rules[0]->markReferenced();
- $this->isReferenced = true;
- Less_Tree::ReferencedArray($this->rules[0]->rules);
- }
-
- // evaltop
- public function compileTop($env)
- {
- $result = $this;
-
- if (count($env->mediaBlocks) > 1) {
- $selectors = $this->emptySelectors();
- $result = new Less_Tree_Ruleset($selectors, $env->mediaBlocks);
- $result->multiMedia = true;
- }
-
- $env->mediaBlocks = array();
- $env->mediaPath = array();
-
- return $result;
- }
-
- public function compileNested($env)
- {
- $path = array_merge($env->mediaPath, array($this));
-
- // Extract the media-query conditions separated with `,` (OR).
- foreach ($path as $key => $p) {
- $value = $p->features instanceof Less_Tree_Value ? $p->features->value : $p->features;
- $path[$key] = is_array($value) ? $value : array($value);
- }
-
- // Trace all permutations to generate the resulting media-query.
- //
- // (a, b and c) with nested (d, e) ->
- // a and d
- // a and e
- // b and c and d
- // b and c and e
-
- $permuted = $this->permute($path);
- $expressions = array();
- foreach ($permuted as $path) {
-
- for ($i = 0, $len = count($path); $i < $len; $i++) {
- $path[$i] = Less_Parser::is_method($path[$i], 'toCSS') ? $path[$i] : new Less_Tree_Anonymous($path[$i]);
- }
-
- for ($i = count($path) - 1; $i > 0; $i--) {
- array_splice($path, $i, 0, array(new Less_Tree_Anonymous('and')));
- }
-
- $expressions[] = new Less_Tree_Expression($path);
- }
- $this->features = new Less_Tree_Value($expressions);
-
-
- // Fake a tree-node that doesn't output anything.
- return new Less_Tree_Ruleset(array(), array());
- }
-
- public function permute($arr)
- {
- if (!$arr)
- return array();
-
- if (count($arr) == 1)
- return $arr[0];
-
- $result = array();
- $rest = $this->permute(array_slice($arr, 1));
- foreach ($rest as $r) {
- foreach ($arr[0] as $a) {
- $result[] = array_merge(
- is_array($a) ? $a : array($a),
- is_array($r) ? $r : array($r)
- );
- }
- }
-
- return $result;
- }
-
- public function bubbleSelectors($selectors)
- {
-
- if (!$selectors) return;
-
- $this->rules = array(new Less_Tree_Ruleset($selectors, array($this->rules[0])));
- }
-
-}
-
-
-/**
- * A simple css name-value pair
- * ex: width:100px;
- *
- * In bootstrap, there are about 600-1,000 simple name-value pairs (depending on how forgiving the match is) -vs- 6,020 dynamic rules (Less_Tree_Rule)
- * Using the name-value object can speed up bootstrap compilation slightly, but it breaks color keyword interpretation: color:red -> color:#FF0000;
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_NameValue extends Less_Tree
-{
-
- public $name;
- public $value;
- public $index;
- public $currentFileInfo;
- public $type = 'NameValue';
-
- public function __construct($name, $value = null, $index = null, $currentFileInfo = null)
- {
- $this->name = $name;
- $this->value = $value;
- $this->index = $index;
- $this->currentFileInfo = $currentFileInfo;
- }
-
- public function genCSS($output)
- {
-
- $output->add(
- $this->name
- . Less_Environment::$_outputMap[': ']
- . $this->value
- . (((Less_Environment::$lastRule && Less_Parser::$options['compress'])) ? "" : ";")
- , $this->currentFileInfo, $this->index);
- }
-
- public function compile($env)
- {
- return $this;
- }
-}
-
-
-/**
- * Negative
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Negative extends Less_Tree
-{
-
- public $value;
- public $type = 'Negative';
-
- public function __construct($node)
- {
- $this->value = $node;
- }
-
- //function accept($visitor) {
- // $this->value = $visitor->visit($this->value);
- //}
-
- /**
- * @see Less_Tree::genCSS
- */
- public function genCSS($output)
- {
- $output->add('-');
- $this->value->genCSS($output);
- }
-
- public function compile($env)
- {
- if (Less_Environment::isMathOn()) {
- $ret = new Less_Tree_Operation('*', array(new Less_Tree_Dimension(-1), $this->value));
- return $ret->compile($env);
- }
- return new Less_Tree_Negative($this->value->compile($env));
- }
-}
-
-/**
- * Operation
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Operation extends Less_Tree
-{
-
- public $op;
- public $operands;
- public $isSpaced;
- public $type = 'Operation';
-
- /**
- * @param string $op
- */
- public function __construct($op, $operands, $isSpaced = false)
- {
- $this->op = trim($op);
- $this->operands = $operands;
- $this->isSpaced = $isSpaced;
- }
-
- public function accept($visitor)
- {
- $this->operands = $visitor->visitArray($this->operands);
- }
-
- public function compile($env)
- {
- $a = $this->operands[0]->compile($env);
- $b = $this->operands[1]->compile($env);
-
-
- if (Less_Environment::isMathOn()) {
-
- if ($a instanceof Less_Tree_Dimension && $b instanceof Less_Tree_Color) {
- $a = $a->toColor();
-
- } elseif ($b instanceof Less_Tree_Dimension && $a instanceof Less_Tree_Color) {
- $b = $b->toColor();
-
- }
-
- if (!method_exists($a, 'operate')) {
- throw new Less_Exception_Compiler("Operation on an invalid type");
- }
-
- return $a->operate($this->op, $b);
- }
-
- return new Less_Tree_Operation($this->op, array($a, $b), $this->isSpaced);
- }
-
-
- /**
- * @see Less_Tree::genCSS
- */
- public function genCSS($output)
- {
- $this->operands[0]->genCSS($output);
- if ($this->isSpaced) {
- $output->add(" ");
- }
- $output->add($this->op);
- if ($this->isSpaced) {
- $output->add(' ');
- }
- $this->operands[1]->genCSS($output);
- }
-
-}
-
-
-/**
- * Paren
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Paren extends Less_Tree
-{
-
- public $value;
- public $type = 'Paren';
-
- public function __construct($value)
- {
- $this->value = $value;
- }
-
- public function accept($visitor)
- {
- $this->value = $visitor->visitObj($this->value);
- }
-
- /**
- * @see Less_Tree::genCSS
- */
- public function genCSS($output)
- {
- $output->add('(');
- $this->value->genCSS($output);
- $output->add(')');
- }
-
- public function compile($env)
- {
- return new Less_Tree_Paren($this->value->compile($env));
- }
-
-}
-
-
-/**
- * Quoted
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Quoted extends Less_Tree
-{
- public $escaped;
- public $value;
- public $quote;
- public $index;
- public $currentFileInfo;
- public $type = 'Quoted';
-
- /**
- * @param string $str
- */
- public function __construct($str, $content = '', $escaped = false, $index = false, $currentFileInfo = null)
- {
- $this->escaped = $escaped;
- $this->value = $content;
- if ($str) {
- $this->quote = $str[0];
- }
- $this->index = $index;
- $this->currentFileInfo = $currentFileInfo;
- }
-
- /**
- * @see Less_Tree::genCSS
- */
- public function genCSS($output)
- {
- if (!$this->escaped) {
- $output->add($this->quote, $this->currentFileInfo, $this->index);
- }
- $output->add($this->value);
- if (!$this->escaped) {
- $output->add($this->quote);
- }
- }
-
- public function compile($env)
- {
-
- $value = $this->value;
- if (preg_match_all('/`([^`]+)`/', $this->value, $matches)) {
- foreach ($matches as $i => $match) {
- $js = new Less_Tree_JavaScript($matches[1], $this->index, true);
- $js = $js->compile()->value;
- $value = str_replace($matches[0][$i], $js, $value);
- }
- }
-
- if (preg_match_all('/@\{([\w-]+)\}/', $value, $matches)) {
- foreach ($matches[1] as $i => $match) {
- $v = new Less_Tree_Variable('@' . $match, $this->index, $this->currentFileInfo);
- $v = $v->compile($env);
- $v = ($v instanceof Less_Tree_Quoted) ? $v->value : $v->toCSS();
- $value = str_replace($matches[0][$i], $v, $value);
- }
- }
-
- return new Less_Tree_Quoted($this->quote . $value . $this->quote, $value, $this->escaped, $this->index, $this->currentFileInfo);
- }
-
- public function compare($x)
- {
-
- if (!Less_Parser::is_method($x, 'toCSS')) {
- return -1;
- }
-
- $left = $this->toCSS();
- $right = $x->toCSS();
-
- if ($left === $right) {
- return 0;
- }
-
- return $left < $right ? -1 : 1;
- }
-}
-
-
-/**
- * Rule
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Rule extends Less_Tree
-{
-
- public $name;
- public $value;
- public $important;
- public $merge;
- public $index;
- public $inline;
- public $variable;
- public $currentFileInfo;
- public $type = 'Rule';
-
- /**
- * @param string $important
- */
- public function __construct($name, $value = null, $important = null, $merge = null, $index = null, $currentFileInfo = null, $inline = false)
- {
- $this->name = $name;
- $this->value = ($value instanceof Less_Tree_Value || $value instanceof Less_Tree_Ruleset) ? $value : new Less_Tree_Value(array($value));
- $this->important = $important ? ' ' . trim($important) : '';
- $this->merge = $merge;
- $this->index = $index;
- $this->currentFileInfo = $currentFileInfo;
- $this->inline = $inline;
- $this->variable = (is_string($name) && $name[0] === '@');
- }
-
- public function accept($visitor)
- {
- $this->value = $visitor->visitObj($this->value);
- }
-
- /**
- * @see Less_Tree::genCSS
- */
- public function genCSS($output)
- {
-
- $output->add($this->name . Less_Environment::$_outputMap[': '], $this->currentFileInfo, $this->index);
- try {
- $this->value->genCSS($output);
-
- } catch (Less_Exception_Parser $e) {
- $e->index = $this->index;
- $e->currentFile = $this->currentFileInfo;
- throw $e;
- }
- $output->add($this->important . (($this->inline || (Less_Environment::$lastRule && Less_Parser::$options['compress'])) ? "" : ";"), $this->currentFileInfo, $this->index);
- }
-
- public function compile($env)
- {
-
- $name = $this->name;
- if (is_array($name)) {
- // expand 'primitive' name directly to get
- // things faster (~10% for benchmark.less):
- if (count($name) === 1 && $name[0] instanceof Less_Tree_Keyword) {
- $name = $name[0]->value;
- } else {
- $name = $this->CompileName($env, $name);
- }
- }
-
- $strictMathBypass = Less_Parser::$options['strictMath'];
- if ($name === "font" && !Less_Parser::$options['strictMath']) {
- Less_Parser::$options['strictMath'] = true;
- }
-
- try {
- $evaldValue = $this->value->compile($env);
-
- if (!$this->variable && $evaldValue->type === "DetachedRuleset") {
- throw new Less_Exception_Compiler("Rulesets cannot be evaluated on a property.", null, $this->index, $this->currentFileInfo);
- }
-
- if (Less_Environment::$mixin_stack) {
- $return = new Less_Tree_Rule($name, $evaldValue, $this->important, $this->merge, $this->index, $this->currentFileInfo, $this->inline);
- } else {
- $this->name = $name;
- $this->value = $evaldValue;
- $return = $this;
- }
-
- } catch (Less_Exception_Parser $e) {
- if (!is_numeric($e->index)) {
- $e->index = $this->index;
- $e->currentFile = $this->currentFileInfo;
- }
- throw $e;
- }
-
- Less_Parser::$options['strictMath'] = $strictMathBypass;
-
- return $return;
- }
-
-
- public function CompileName($env, $name)
- {
- $output = new Less_Output();
- foreach ($name as $n) {
- $n->compile($env)->genCSS($output);
- }
- return $output->toString();
- }
-
- public function makeImportant()
- {
- return new Less_Tree_Rule($this->name, $this->value, '!important', $this->merge, $this->index, $this->currentFileInfo, $this->inline);
- }
-
-}
-
-
-/**
- * Ruleset
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Ruleset extends Less_Tree
-{
-
- protected $lookups;
- public $_variables;
- public $_rulesets;
-
- public $strictImports;
-
- public $selectors;
- public $rules;
- public $root;
- public $allowImports;
- public $paths;
- public $firstRoot;
- public $type = 'Ruleset';
- public $multiMedia;
- public $allExtends;
-
- public $ruleset_id;
- public $originalRuleset;
-
- public $first_oelements;
-
- public function SetRulesetIndex()
- {
- $this->ruleset_id = Less_Parser::$next_id++;
- $this->originalRuleset = $this->ruleset_id;
-
- if ($this->selectors) {
- foreach ($this->selectors as $sel) {
- if ($sel->_oelements) {
- $this->first_oelements[$sel->_oelements[0]] = true;
- }
- }
- }
- }
-
- public function __construct($selectors, $rules, $strictImports = null)
- {
- $this->selectors = $selectors;
- $this->rules = $rules;
- $this->lookups = array();
- $this->strictImports = $strictImports;
- $this->SetRulesetIndex();
- }
-
- public function accept($visitor)
- {
- if ($this->paths) {
- $paths_len = count($this->paths);
- for ($i = 0, $paths_len; $i < $paths_len; $i++) {
- $this->paths[$i] = $visitor->visitArray($this->paths[$i]);
- }
- } elseif ($this->selectors) {
- $this->selectors = $visitor->visitArray($this->selectors);
- }
-
- if ($this->rules) {
- $this->rules = $visitor->visitArray($this->rules);
- }
- }
-
- public function compile($env)
- {
-
- $ruleset = $this->PrepareRuleset($env);
-
-
- // Store the frames around mixin definitions,
- // so they can be evaluated like closures when the time comes.
- $rsRuleCnt = count($ruleset->rules);
- for ($i = 0; $i < $rsRuleCnt; $i++) {
- if ($ruleset->rules[$i] instanceof Less_Tree_Mixin_Definition || $ruleset->rules[$i] instanceof Less_Tree_DetachedRuleset) {
- $ruleset->rules[$i] = $ruleset->rules[$i]->compile($env);
- }
- }
-
- $mediaBlockCount = 0;
- if ($env instanceof Less_Environment) {
- $mediaBlockCount = count($env->mediaBlocks);
- }
-
- // Evaluate mixin calls.
- $this->EvalMixinCalls($ruleset, $env, $rsRuleCnt);
-
-
- // Evaluate everything else
- for ($i = 0; $i < $rsRuleCnt; $i++) {
- if (!($ruleset->rules[$i] instanceof Less_Tree_Mixin_Definition || $ruleset->rules[$i] instanceof Less_Tree_DetachedRuleset)) {
- $ruleset->rules[$i] = $ruleset->rules[$i]->compile($env);
- }
- }
-
- // Evaluate everything else
- for ($i = 0; $i < $rsRuleCnt; $i++) {
- $rule = $ruleset->rules[$i];
-
- // for rulesets, check if it is a css guard and can be removed
- if ($rule instanceof Less_Tree_Ruleset && $rule->selectors && count($rule->selectors) === 1) {
-
- // check if it can be folded in (e.g. & where)
- if ($rule->selectors[0]->isJustParentSelector()) {
- array_splice($ruleset->rules, $i--, 1);
- $rsRuleCnt--;
-
- for ($j = 0; $j < count($rule->rules); $j++) {
- $subRule = $rule->rules[$j];
- if (!($subRule instanceof Less_Tree_Rule) || !$subRule->variable) {
- array_splice($ruleset->rules, ++$i, 0, array($subRule));
- $rsRuleCnt++;
- }
- }
-
- }
- }
- }
-
-
- // Pop the stack
- $env->shiftFrame();
-
- if ($mediaBlockCount) {
- $len = count($env->mediaBlocks);
- for ($i = $mediaBlockCount; $i < $len; $i++) {
- $env->mediaBlocks[$i]->bubbleSelectors($ruleset->selectors);
- }
- }
-
- return $ruleset;
- }
-
- /**
- * Compile Less_Tree_Mixin_Call objects
- *
- * @param Less_Tree_Ruleset $ruleset
- * @param integer $rsRuleCnt
- */
- private function EvalMixinCalls($ruleset, $env, &$rsRuleCnt)
- {
- for ($i = 0; $i < $rsRuleCnt; $i++) {
- $rule = $ruleset->rules[$i];
-
- if ($rule instanceof Less_Tree_Mixin_Call) {
- $rule = $rule->compile($env);
-
- $temp = array();
- foreach ($rule as $r) {
- if (($r instanceof Less_Tree_Rule) && $r->variable) {
- // do not pollute the scope if the variable is
- // already there. consider returning false here
- // but we need a way to "return" variable from mixins
- if (!$ruleset->variable($r->name)) {
- $temp[] = $r;
- }
- } else {
- $temp[] = $r;
- }
- }
- $temp_count = count($temp) - 1;
- array_splice($ruleset->rules, $i, 1, $temp);
- $rsRuleCnt += $temp_count;
- $i += $temp_count;
- $ruleset->resetCache();
-
- } elseif ($rule instanceof Less_Tree_RulesetCall) {
-
- $rule = $rule->compile($env);
- $rules = array();
- foreach ($rule->rules as $r) {
- if (($r instanceof Less_Tree_Rule) && $r->variable) {
- continue;
- }
- $rules[] = $r;
- }
-
- array_splice($ruleset->rules, $i, 1, $rules);
- $temp_count = count($rules);
- $rsRuleCnt += $temp_count - 1;
- $i += $temp_count - 1;
- $ruleset->resetCache();
- }
-
- }
- }
-
-
- /**
- * Compile the selectors and create a new ruleset object for the compile() method
- *
- */
- private function PrepareRuleset($env)
- {
-
- $hasOnePassingSelector = false;
- $selectors = array();
- if ($this->selectors) {
- Less_Tree_DefaultFunc::error("it is currently only allowed in parametric mixin guards,");
-
- foreach ($this->selectors as $s) {
- $selector = $s->compile($env);
- $selectors[] = $selector;
- if ($selector->evaldCondition) {
- $hasOnePassingSelector = true;
- }
- }
-
- Less_Tree_DefaultFunc::reset();
- } else {
- $hasOnePassingSelector = true;
- }
-
- if ($this->rules && $hasOnePassingSelector) {
- $rules = $this->rules;
- } else {
- $rules = array();
- }
-
- $ruleset = new Less_Tree_Ruleset($selectors, $rules, $this->strictImports);
-
- $ruleset->originalRuleset = $this->ruleset_id;
-
- $ruleset->root = $this->root;
- $ruleset->firstRoot = $this->firstRoot;
- $ruleset->allowImports = $this->allowImports;
-
-
- // push the current ruleset to the frames stack
- $env->unshiftFrame($ruleset);
-
-
- // Evaluate imports
- if ($ruleset->root || $ruleset->allowImports || !$ruleset->strictImports) {
- $ruleset->evalImports($env);
- }
-
- return $ruleset;
- }
-
- function evalImports($env)
- {
-
- $rules_len = count($this->rules);
- for ($i = 0; $i < $rules_len; $i++) {
- $rule = $this->rules[$i];
-
- if ($rule instanceof Less_Tree_Import) {
- $rules = $rule->compile($env);
- if (is_array($rules)) {
- array_splice($this->rules, $i, 1, $rules);
- $temp_count = count($rules) - 1;
- $i += $temp_count;
- $rules_len += $temp_count;
- } else {
- array_splice($this->rules, $i, 1, array($rules));
- }
-
- $this->resetCache();
- }
- }
- }
-
- function makeImportant()
- {
-
- $important_rules = array();
- foreach ($this->rules as $rule) {
- if ($rule instanceof Less_Tree_Rule || $rule instanceof Less_Tree_Ruleset) {
- $important_rules[] = $rule->makeImportant();
- } else {
- $important_rules[] = $rule;
- }
- }
-
- return new Less_Tree_Ruleset($this->selectors, $important_rules, $this->strictImports);
- }
-
- public function matchArgs($args)
- {
- return !$args;
- }
-
- // lets you call a css selector with a guard
- public function matchCondition($args, $env)
- {
- $lastSelector = end($this->selectors);
-
- if (!$lastSelector->evaldCondition) {
- return false;
- }
- if ($lastSelector->condition && !$lastSelector->condition->compile($env->copyEvalEnv($env->frames))) {
- return false;
- }
- return true;
- }
-
- function resetCache()
- {
- $this->_rulesets = null;
- $this->_variables = null;
- $this->lookups = array();
- }
-
- public function variables()
- {
- $this->_variables = array();
- foreach ($this->rules as $r) {
- if ($r instanceof Less_Tree_Rule && $r->variable === true) {
- $this->_variables[$r->name] = $r;
- }
- }
- }
-
- public function variable($name)
- {
-
- if (is_null($this->_variables)) {
- $this->variables();
- }
- return isset($this->_variables[$name]) ? $this->_variables[$name] : null;
- }
-
- public function find($selector, $self = null)
- {
-
- $key = implode(' ', $selector->_oelements);
-
- if (!isset($this->lookups[$key])) {
-
- if (!$self) {
- $self = $this->ruleset_id;
- }
-
- $this->lookups[$key] = array();
-
- $first_oelement = $selector->_oelements[0];
-
- foreach ($this->rules as $rule) {
- if ($rule instanceof Less_Tree_Ruleset && $rule->ruleset_id != $self) {
-
- if (isset($rule->first_oelements[$first_oelement])) {
-
- foreach ($rule->selectors as $ruleSelector) {
- $match = $selector->match($ruleSelector);
- if ($match) {
- if ($selector->elements_len > $match) {
- $this->lookups[$key] = array_merge($this->lookups[$key], $rule->find(new Less_Tree_Selector(array_slice($selector->elements, $match)), $self));
- } else {
- $this->lookups[$key][] = $rule;
- }
- break;
- }
- }
- }
- }
- }
- }
-
- return $this->lookups[$key];
- }
-
-
- /**
- * @see Less_Tree::genCSS
- */
- public function genCSS($output)
- {
-
- if (!$this->root) {
- Less_Environment::$tabLevel++;
- }
-
- $tabRuleStr = $tabSetStr = '';
- if (!Less_Parser::$options['compress']) {
- if (Less_Environment::$tabLevel) {
- $tabRuleStr = "\n" . str_repeat(' ', Less_Environment::$tabLevel);
- $tabSetStr = "\n" . str_repeat(' ', Less_Environment::$tabLevel - 1);
- } else {
- $tabSetStr = $tabRuleStr = "\n";
- }
- }
-
-
- $ruleNodes = array();
- $rulesetNodes = array();
- foreach ($this->rules as $rule) {
-
- $class = get_class($rule);
- if (($class === 'Less_Tree_Media') || ($class === 'Less_Tree_Directive') || ($this->root && $class === 'Less_Tree_Comment') || ($class === 'Less_Tree_Ruleset' && $rule->rules)) {
- $rulesetNodes[] = $rule;
- } else {
- $ruleNodes[] = $rule;
- }
- }
-
- // If this is the root node, we don't render
- // a selector, or {}.
- if (!$this->root) {
-
- /*
- debugInfo = tree.debugInfo(env, this, tabSetStr);
-
- if (debugInfo) {
- output.add(debugInfo);
- output.add(tabSetStr);
- }
- */
-
- $paths_len = count($this->paths);
- for ($i = 0; $i < $paths_len; $i++) {
- $path = $this->paths[$i];
- $firstSelector = true;
-
- foreach ($path as $p) {
- $p->genCSS($output, $firstSelector);
- $firstSelector = false;
- }
-
- if ($i + 1 < $paths_len) {
- $output->add(',' . $tabSetStr);
- }
- }
-
- $output->add((Less_Parser::$options['compress'] ? '{' : " {") . $tabRuleStr);
- }
-
- // Compile rules and rulesets
- $ruleNodes_len = count($ruleNodes);
- $rulesetNodes_len = count($rulesetNodes);
- for ($i = 0; $i < $ruleNodes_len; $i++) {
- $rule = $ruleNodes[$i];
-
- // @page{ directive ends up with root elements inside it, a mix of rules and rulesets
- // In this instance we do not know whether it is the last property
- if ($i + 1 === $ruleNodes_len && (!$this->root || $rulesetNodes_len === 0 || $this->firstRoot)) {
- Less_Environment::$lastRule = true;
- }
-
- $rule->genCSS($output);
-
- if (!Less_Environment::$lastRule) {
- $output->add($tabRuleStr);
- } else {
- Less_Environment::$lastRule = false;
- }
- }
-
- if (!$this->root) {
- $output->add($tabSetStr . '}');
- Less_Environment::$tabLevel--;
- }
-
- $firstRuleset = true;
- $space = ($this->root ? $tabRuleStr : $tabSetStr);
- for ($i = 0; $i < $rulesetNodes_len; $i++) {
-
- if ($ruleNodes_len && $firstRuleset) {
- $output->add($space);
- } elseif (!$firstRuleset) {
- $output->add($space);
- }
- $firstRuleset = false;
- $rulesetNodes[$i]->genCSS($output);
- }
-
- if (!Less_Parser::$options['compress'] && $this->firstRoot) {
- $output->add("\n");
- }
-
- }
-
-
- function markReferenced()
- {
- if (!$this->selectors) {
- return;
- }
- foreach ($this->selectors as $selector) {
- $selector->markReferenced();
- }
- }
-
- public function joinSelectors($context, $selectors)
- {
- $paths = array();
- if (is_array($selectors)) {
- foreach ($selectors as $selector) {
- $this->joinSelector($paths, $context, $selector);
- }
- }
- return $paths;
- }
-
- public function joinSelector(&$paths, $context, $selector)
- {
-
- $hasParentSelector = false;
-
- foreach ($selector->elements as $el) {
- if ($el->value === '&') {
- $hasParentSelector = true;
- }
- }
-
- if (!$hasParentSelector) {
- if ($context) {
- foreach ($context as $context_el) {
- $paths[] = array_merge($context_el, array($selector));
- }
- } else {
- $paths[] = array($selector);
- }
- return;
- }
-
-
- // The paths are [[Selector]]
- // The first list is a list of comma seperated selectors
- // The inner list is a list of inheritance seperated selectors
- // e.g.
- // .a, .b {
- // .c {
- // }
- // }
- // == [[.a] [.c]] [[.b] [.c]]
- //
-
- // the elements from the current selector so far
- $currentElements = array();
- // the current list of new selectors to add to the path.
- // We will build it up. We initiate it with one empty selector as we "multiply" the new selectors
- // by the parents
- $newSelectors = array(array());
-
-
- foreach ($selector->elements as $el) {
-
- // non parent reference elements just get added
- if ($el->value !== '&') {
- $currentElements[] = $el;
- } else {
- // the new list of selectors to add
- $selectorsMultiplied = array();
-
- // merge the current list of non parent selector elements
- // on to the current list of selectors to add
- if ($currentElements) {
- $this->mergeElementsOnToSelectors($currentElements, $newSelectors);
- }
-
- // loop through our current selectors
- foreach ($newSelectors as $sel) {
-
- // if we don't have any parent paths, the & might be in a mixin so that it can be used
- // whether there are parents or not
- if (!$context) {
- // the combinator used on el should now be applied to the next element instead so that
- // it is not lost
- if ($sel) {
- $sel[0]->elements = array_slice($sel[0]->elements, 0);
- $sel[0]->elements[] = new Less_Tree_Element($el->combinator, '', $el->index, $el->currentFileInfo);
- }
- $selectorsMultiplied[] = $sel;
- } else {
-
- // and the parent selectors
- foreach ($context as $parentSel) {
- // We need to put the current selectors
- // then join the last selector's elements on to the parents selectors
-
- // our new selector path
- $newSelectorPath = array();
- // selectors from the parent after the join
- $afterParentJoin = array();
- $newJoinedSelectorEmpty = true;
-
- //construct the joined selector - if & is the first thing this will be empty,
- // if not newJoinedSelector will be the last set of elements in the selector
- if ($sel) {
- $newSelectorPath = $sel;
- $lastSelector = array_pop($newSelectorPath);
- $newJoinedSelector = $selector->createDerived(array_slice($lastSelector->elements, 0));
- $newJoinedSelectorEmpty = false;
- } else {
- $newJoinedSelector = $selector->createDerived(array());
- }
-
- //put together the parent selectors after the join
- if (count($parentSel) > 1) {
- $afterParentJoin = array_merge($afterParentJoin, array_slice($parentSel, 1));
- }
-
- if ($parentSel) {
- $newJoinedSelectorEmpty = false;
-
- // join the elements so far with the first part of the parent
- $newJoinedSelector->elements[] = new Less_Tree_Element($el->combinator, $parentSel[0]->elements[0]->value, $el->index, $el->currentFileInfo);
-
- $newJoinedSelector->elements = array_merge($newJoinedSelector->elements, array_slice($parentSel[0]->elements, 1));
- }
-
- if (!$newJoinedSelectorEmpty) {
- // now add the joined selector
- $newSelectorPath[] = $newJoinedSelector;
- }
-
- // and the rest of the parent
- $newSelectorPath = array_merge($newSelectorPath, $afterParentJoin);
-
- // add that to our new set of selectors
- $selectorsMultiplied[] = $newSelectorPath;
- }
- }
- }
-
- // our new selectors has been multiplied, so reset the state
- $newSelectors = $selectorsMultiplied;
- $currentElements = array();
- }
- }
-
- // if we have any elements left over (e.g. .a& .b == .b)
- // add them on to all the current selectors
- if ($currentElements) {
- $this->mergeElementsOnToSelectors($currentElements, $newSelectors);
- }
- foreach ($newSelectors as $new_sel) {
- if ($new_sel) {
- $paths[] = $new_sel;
- }
- }
- }
-
- function mergeElementsOnToSelectors($elements, &$selectors)
- {
-
- if (!$selectors) {
- $selectors[] = array(new Less_Tree_Selector($elements));
- return;
- }
-
-
- foreach ($selectors as &$sel) {
-
- // if the previous thing in sel is a parent this needs to join on to it
- if ($sel) {
- $last = count($sel) - 1;
- $sel[$last] = $sel[$last]->createDerived(array_merge($sel[$last]->elements, $elements));
- } else {
- $sel[] = new Less_Tree_Selector($elements);
- }
- }
- }
-}
-
-
-/**
- * RulesetCall
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_RulesetCall extends Less_Tree
-{
-
- public $variable;
- public $type = "RulesetCall";
-
- public function __construct($variable)
- {
- $this->variable = $variable;
- }
-
- public function accept($visitor)
- {
- }
-
- public function compile($env)
- {
- $variable = new Less_Tree_Variable($this->variable);
- $detachedRuleset = $variable->compile($env);
- return $detachedRuleset->callEval($env);
- }
-}
-
-
-/**
- * Selector
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Selector extends Less_Tree
-{
-
- public $elements;
- public $condition;
- public $extendList = array();
- public $_css;
- public $index;
- public $evaldCondition = false;
- public $type = 'Selector';
- public $currentFileInfo = array();
- public $isReferenced;
- public $mediaEmpty;
-
- public $elements_len = 0;
-
- public $_oelements;
- public $_oelements_len;
- public $cacheable = true;
-
- /**
- * @param boolean $isReferenced
- */
- public function __construct($elements, $extendList = array(), $condition = null, $index = null, $currentFileInfo = null, $isReferenced = null)
- {
-
- $this->elements = $elements;
- $this->elements_len = count($elements);
- $this->extendList = $extendList;
- $this->condition = $condition;
- if ($currentFileInfo) {
- $this->currentFileInfo = $currentFileInfo;
- }
- $this->isReferenced = $isReferenced;
- if (!$condition) {
- $this->evaldCondition = true;
- }
-
- $this->CacheElements();
- }
-
- public function accept($visitor)
- {
- $this->elements = $visitor->visitArray($this->elements);
- $this->extendList = $visitor->visitArray($this->extendList);
- if ($this->condition) {
- $this->condition = $visitor->visitObj($this->condition);
- }
-
- if ($visitor instanceof Less_Visitor_extendFinder) {
- $this->CacheElements();
- }
- }
-
- public function createDerived($elements, $extendList = null, $evaldCondition = null)
- {
- $newSelector = new Less_Tree_Selector($elements, ($extendList ? $extendList : $this->extendList), null, $this->index, $this->currentFileInfo, $this->isReferenced);
- $newSelector->evaldCondition = $evaldCondition ? $evaldCondition : $this->evaldCondition;
- return $newSelector;
- }
-
-
- public function match($other)
- {
-
- if (!$other->_oelements || ($this->elements_len < $other->_oelements_len)) {
- return 0;
- }
-
- for ($i = 0; $i < $other->_oelements_len; $i++) {
- if ($this->elements[$i]->value !== $other->_oelements[$i]) {
- return 0;
- }
- }
-
- return $other->_oelements_len; // return number of matched elements
- }
-
-
- public function CacheElements()
- {
-
- $this->_oelements = array();
- $css = '';
-
- foreach ($this->elements as $v) {
-
- $css .= $v->combinator;
- if (!$v->value_is_object) {
- $css .= $v->value;
- continue;
- }
-
- if (!property_exists($v->value, 'value') || !is_string($v->value->value)) {
- $this->cacheable = false;
- return;
- }
- $css .= $v->value->value;
- }
-
- $this->_oelements_len = preg_match_all('/[,\.\w-](?:[\w-]|(?:\\\\.))*/', $css, $matches);
- if ($this->_oelements_len) {
- $this->_oelements = $matches[0];
-
- if ($this->_oelements[0] === '&') {
- array_shift($this->_oelements);
- $this->_oelements_len--;
- }
- }
- }
-
- public function isJustParentSelector()
- {
- return !$this->mediaEmpty &&
- count($this->elements) === 1 &&
- $this->elements[0]->value === '&' &&
- ($this->elements[0]->combinator === ' ' || $this->elements[0]->combinator === '');
- }
-
- public function compile($env)
- {
-
- $elements = array();
- foreach ($this->elements as $el) {
- $elements[] = $el->compile($env);
- }
-
- $extendList = array();
- foreach ($this->extendList as $el) {
- $extendList[] = $el->compile($el);
- }
-
- $evaldCondition = false;
- if ($this->condition) {
- $evaldCondition = $this->condition->compile($env);
- }
-
- return $this->createDerived($elements, $extendList, $evaldCondition);
- }
-
-
- /**
- * @see Less_Tree::genCSS
- */
- public function genCSS($output, $firstSelector = true)
- {
-
- if (!$firstSelector && $this->elements[0]->combinator === "") {
- $output->add(' ', $this->currentFileInfo, $this->index);
- }
-
- foreach ($this->elements as $element) {
- $element->genCSS($output);
- }
- }
-
- public function markReferenced()
- {
- $this->isReferenced = true;
- }
-
- public function getIsReferenced()
- {
- return !isset($this->currentFileInfo['reference']) || !$this->currentFileInfo['reference'] || $this->isReferenced;
- }
-
- public function getIsOutput()
- {
- return $this->evaldCondition;
- }
-
-}
-
-
-/**
- * UnicodeDescriptor
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_UnicodeDescriptor extends Less_Tree
-{
-
- public $value;
- public $type = 'UnicodeDescriptor';
-
- public function __construct($value)
- {
- $this->value = $value;
- }
-
- /**
- * @see Less_Tree::genCSS
- */
- public function genCSS($output)
- {
- $output->add($this->value);
- }
-
- public function compile()
- {
- return $this;
- }
-}
-
-
-/**
- * Unit
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Unit extends Less_Tree
-{
-
- var $numerator = array();
- var $denominator = array();
- public $backupUnit;
- public $type = 'Unit';
-
- public function __construct($numerator = array(), $denominator = array(), $backupUnit = null)
- {
- $this->numerator = $numerator;
- $this->denominator = $denominator;
- $this->backupUnit = $backupUnit;
- }
-
- public function __clone()
- {
- }
-
- /**
- * @see Less_Tree::genCSS
- */
- public function genCSS($output)
- {
-
- if ($this->numerator) {
- $output->add($this->numerator[0]);
- } elseif ($this->denominator) {
- $output->add($this->denominator[0]);
- } elseif (!Less_Parser::$options['strictUnits'] && $this->backupUnit) {
- $output->add($this->backupUnit);
- return;
- }
- }
-
- public function toString()
- {
- $returnStr = implode('*', $this->numerator);
- foreach ($this->denominator as $d) {
- $returnStr .= '/' . $d;
- }
- return $returnStr;
- }
-
- public function __toString()
- {
- return $this->toString();
- }
-
-
- /**
- * @param Less_Tree_Unit $other
- */
- public function compare($other)
- {
- return $this->is($other->toString()) ? 0 : -1;
- }
-
- public function is($unitString)
- {
- return $this->toString() === $unitString;
- }
-
- public function isLength()
- {
- $css = $this->toCSS();
- return !!preg_match('/px|em|%|in|cm|mm|pc|pt|ex/', $css);
- }
-
- public function isAngle()
- {
- return isset(Less_Tree_UnitConversions::$angle[$this->toCSS()]);
- }
-
- public function isEmpty()
- {
- return !$this->numerator && !$this->denominator;
- }
-
- public function isSingular()
- {
- return count($this->numerator) <= 1 && !$this->denominator;
- }
-
-
- public function usedUnits()
- {
- $result = array();
-
- foreach (Less_Tree_UnitConversions::$groups as $groupName) {
- $group = Less_Tree_UnitConversions::${$groupName};
-
- foreach ($this->numerator as $atomicUnit) {
- if (isset($group[$atomicUnit]) && !isset($result[$groupName])) {
- $result[$groupName] = $atomicUnit;
- }
- }
-
- foreach ($this->denominator as $atomicUnit) {
- if (isset($group[$atomicUnit]) && !isset($result[$groupName])) {
- $result[$groupName] = $atomicUnit;
- }
- }
- }
-
- return $result;
- }
-
- public function cancel()
- {
- $counter = array();
- $backup = null;
-
- foreach ($this->numerator as $atomicUnit) {
- if (!$backup) {
- $backup = $atomicUnit;
- }
- $counter[$atomicUnit] = (isset($counter[$atomicUnit]) ? $counter[$atomicUnit] : 0) + 1;
- }
-
- foreach ($this->denominator as $atomicUnit) {
- if (!$backup) {
- $backup = $atomicUnit;
- }
- $counter[$atomicUnit] = (isset($counter[$atomicUnit]) ? $counter[$atomicUnit] : 0) - 1;
- }
-
- $this->numerator = array();
- $this->denominator = array();
-
- foreach ($counter as $atomicUnit => $count) {
- if ($count > 0) {
- for ($i = 0; $i < $count; $i++) {
- $this->numerator[] = $atomicUnit;
- }
- } elseif ($count < 0) {
- for ($i = 0; $i < -$count; $i++) {
- $this->denominator[] = $atomicUnit;
- }
- }
- }
-
- if (!$this->numerator && !$this->denominator && $backup) {
- $this->backupUnit = $backup;
- }
-
- sort($this->numerator);
- sort($this->denominator);
- }
-
-
-}
-
-
-/**
- * UnitConversions
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_UnitConversions
-{
-
- public static $groups = array('length', 'duration', 'angle');
-
- public static $length = array(
- 'm' => 1,
- 'cm' => 0.01,
- 'mm' => 0.001,
- 'in' => 0.0254,
- 'px' => 0.000264583, // 0.0254 / 96,
- 'pt' => 0.000352778, // 0.0254 / 72,
- 'pc' => 0.004233333, // 0.0254 / 72 * 12
- );
-
- public static $duration = array(
- 's' => 1,
- 'ms' => 0.001
- );
-
- public static $angle = array(
- 'rad' => 0.1591549430919, // 1/(2*M_PI),
- 'deg' => 0.002777778, // 1/360,
- 'grad' => 0.0025, // 1/400,
- 'turn' => 1
- );
-
-}
-
-/**
- * Url
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Url extends Less_Tree
-{
-
- public $attrs;
- public $value;
- public $currentFileInfo;
- public $isEvald;
- public $type = 'Url';
-
- public function __construct($value, $currentFileInfo = null, $isEvald = null)
- {
- $this->value = $value;
- $this->currentFileInfo = $currentFileInfo;
- $this->isEvald = $isEvald;
- }
-
- public function accept($visitor)
- {
- $this->value = $visitor->visitObj($this->value);
- }
-
- /**
- * @see Less_Tree::genCSS
- */
- public function genCSS($output)
- {
- $output->add('url(');
- $this->value->genCSS($output);
- $output->add(')');
- }
-
- /**
- * @param Less_Functions $ctx
- */
- public function compile($ctx)
- {
- $val = $this->value->compile($ctx);
-
- if (!$this->isEvald) {
- // Add the base path if the URL is relative
- if (Less_Parser::$options['relativeUrls']
- && $this->currentFileInfo
- && is_string($val->value)
- && Less_Environment::isPathRelative($val->value)
- ) {
- $rootpath = $this->currentFileInfo['uri_root'];
- if (!$val->quote) {
- $rootpath = preg_replace('/[\(\)\'"\s]/', '\\$1', $rootpath);
- }
- $val->value = $rootpath . $val->value;
- }
-
- $val->value = Less_Environment::normalizePath($val->value);
- }
-
- // Add cache buster if enabled
- if (Less_Parser::$options['urlArgs']) {
- if (!preg_match('/^\s*data:/', $val->value)) {
- $delimiter = strpos($val->value, '?') === false ? '?' : '&';
- $urlArgs = $delimiter . Less_Parser::$options['urlArgs'];
- $hash_pos = strpos($val->value, '#');
- if ($hash_pos !== false) {
- $val->value = substr_replace($val->value, $urlArgs, $hash_pos, 0);
- } else {
- $val->value .= $urlArgs;
- }
- }
- }
-
- return new Less_Tree_URL($val, $this->currentFileInfo, true);
- }
-
-}
-
-
-/**
- * Value
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Value extends Less_Tree
-{
-
- public $type = 'Value';
- public $value;
-
- public function __construct($value)
- {
- $this->value = $value;
- }
-
- public function accept($visitor)
- {
- $this->value = $visitor->visitArray($this->value);
- }
-
- public function compile($env)
- {
-
- $ret = array();
- $i = 0;
- foreach ($this->value as $i => $v) {
- $ret[] = $v->compile($env);
- }
- if ($i > 0) {
- return new Less_Tree_Value($ret);
- }
- return $ret[0];
- }
-
- /**
- * @see Less_Tree::genCSS
- */
- function genCSS($output)
- {
- $len = count($this->value);
- for ($i = 0; $i < $len; $i++) {
- $this->value[$i]->genCSS($output);
- if ($i + 1 < $len) {
- $output->add(Less_Environment::$_outputMap[',']);
- }
- }
- }
-
-}
-
-
-/**
- * Variable
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Variable extends Less_Tree
-{
-
- public $name;
- public $index;
- public $currentFileInfo;
- public $evaluating = false;
- public $type = 'Variable';
-
- /**
- * @param string $name
- */
- public function __construct($name, $index = null, $currentFileInfo = null)
- {
- $this->name = $name;
- $this->index = $index;
- $this->currentFileInfo = $currentFileInfo;
- }
-
- public function compile($env)
- {
-
- if ($this->name[1] === '@') {
- $v = new Less_Tree_Variable(substr($this->name, 1), $this->index + 1, $this->currentFileInfo);
- $name = '@' . $v->compile($env)->value;
- } else {
- $name = $this->name;
- }
-
- if ($this->evaluating) {
- throw new Less_Exception_Compiler("Recursive variable definition for " . $name, null, $this->index, $this->currentFileInfo);
- }
-
- $this->evaluating = true;
-
- foreach ($env->frames as $frame) {
- if ($v = $frame->variable($name)) {
- $r = $v->value->compile($env);
- $this->evaluating = false;
- return $r;
- }
- }
-
- throw new Less_Exception_Compiler("variable " . $name . " is undefined in file " . $this->currentFileInfo["filename"], null, $this->index);
- }
-
-}
-
-
-class Less_Tree_Mixin_Call extends Less_Tree
-{
-
- public $selector;
- public $arguments;
- public $index;
- public $currentFileInfo;
-
- public $important;
- public $type = 'MixinCall';
-
- /**
- * less.js: tree.mixin.Call
- *
- */
- public function __construct($elements, $args, $index, $currentFileInfo, $important = false)
- {
- $this->selector = new Less_Tree_Selector($elements);
- $this->arguments = $args;
- $this->index = $index;
- $this->currentFileInfo = $currentFileInfo;
- $this->important = $important;
- }
-
- //function accept($visitor){
- // $this->selector = $visitor->visit($this->selector);
- // $this->arguments = $visitor->visit($this->arguments);
- //}
-
-
- public function compile($env)
- {
-
- $rules = array();
- $match = false;
- $isOneFound = false;
- $candidates = array();
- $defaultUsed = false;
- $conditionResult = array();
-
- $args = array();
- foreach ($this->arguments as $a) {
- $args[] = array('name' => $a['name'], 'value' => $a['value']->compile($env));
- }
-
- foreach ($env->frames as $frame) {
-
- $mixins = $frame->find($this->selector);
-
- if (!$mixins) {
- continue;
- }
-
- $isOneFound = true;
- $defNone = 0;
- $defTrue = 1;
- $defFalse = 2;
-
- // To make `default()` function independent of definition order we have two "subpasses" here.
- // At first we evaluate each guard *twice* (with `default() == true` and `default() == false`),
- // and build candidate list with corresponding flags. Then, when we know all possible matches,
- // we make a final decision.
-
- $mixins_len = count($mixins);
- for ($m = 0; $m < $mixins_len; $m++) {
- $mixin = $mixins[$m];
-
- if ($this->IsRecursive($env, $mixin)) {
- continue;
- }
-
- if ($mixin->matchArgs($args, $env)) {
-
- $candidate = array('mixin' => $mixin, 'group' => $defNone);
-
- if ($mixin instanceof Less_Tree_Ruleset) {
-
- for ($f = 0; $f < 2; $f++) {
- Less_Tree_DefaultFunc::value($f);
- $conditionResult[$f] = $mixin->matchCondition($args, $env);
- }
- if ($conditionResult[0] || $conditionResult[1]) {
- if ($conditionResult[0] != $conditionResult[1]) {
- $candidate['group'] = $conditionResult[1] ? $defTrue : $defFalse;
- }
-
- $candidates[] = $candidate;
- }
- } else {
- $candidates[] = $candidate;
- }
-
- $match = true;
- }
- }
-
- Less_Tree_DefaultFunc::reset();
-
-
- $count = array(0, 0, 0);
- for ($m = 0; $m < count($candidates); $m++) {
- $count[$candidates[$m]['group']]++;
- }
-
- if ($count[$defNone] > 0) {
- $defaultResult = $defFalse;
- } else {
- $defaultResult = $defTrue;
- if (($count[$defTrue] + $count[$defFalse]) > 1) {
- throw new Exception('Ambiguous use of `default()` found when matching for `' . $this->format($args) + '`');
- }
- }
-
-
- $candidates_length = count($candidates);
- $length_1 = ($candidates_length == 1);
-
- for ($m = 0; $m < $candidates_length; $m++) {
- $candidate = $candidates[$m]['group'];
- if (($candidate === $defNone) || ($candidate === $defaultResult)) {
- try {
- $mixin = $candidates[$m]['mixin'];
- if (!($mixin instanceof Less_Tree_Mixin_Definition)) {
- $mixin = new Less_Tree_Mixin_Definition('', array(), $mixin->rules, null, false);
- $mixin->originalRuleset = $mixins[$m]->originalRuleset;
- }
- $rules = array_merge($rules, $mixin->evalCall($env, $args, $this->important)->rules);
- } catch (Exception $e) {
- //throw new Less_Exception_Compiler($e->getMessage(), $e->index, null, $this->currentFileInfo['filename']);
- throw new Less_Exception_Compiler($e->getMessage(), null, null, $this->currentFileInfo);
- }
- }
- }
-
- if ($match) {
- if (!$this->currentFileInfo || !isset($this->currentFileInfo['reference']) || !$this->currentFileInfo['reference']) {
- Less_Tree::ReferencedArray($rules);
- }
-
- return $rules;
- }
- }
-
- if ($isOneFound) {
- throw new Less_Exception_Compiler('No matching definition was found for `' . $this->Format($args) . '`', null, $this->index, $this->currentFileInfo);
-
- } else {
- throw new Less_Exception_Compiler(trim($this->selector->toCSS()) . " is undefined in " . $this->currentFileInfo['filename'], null, $this->index);
- }
-
- }
-
- /**
- * Format the args for use in exception messages
- *
- */
- private function Format($args)
- {
- $message = array();
- if ($args) {
- foreach ($args as $a) {
- $argValue = '';
- if ($a['name']) {
- $argValue += $a['name'] + ':';
- }
- if (is_object($a['value'])) {
- $argValue += $a['value']->toCSS();
- } else {
- $argValue += '???';
- }
- $message[] = $argValue;
- }
- }
- return implode(', ', $message);
- }
-
-
- /**
- * Are we in a recursive mixin call?
- *
- * @return bool
- */
- private function IsRecursive($env, $mixin)
- {
-
- foreach ($env->frames as $recur_frame) {
- if (!($mixin instanceof Less_Tree_Mixin_Definition)) {
-
- if ($mixin === $recur_frame) {
- return true;
- }
-
- if (isset($recur_frame->originalRuleset) && $mixin->ruleset_id === $recur_frame->originalRuleset) {
- return true;
- }
- }
- }
-
- return false;
- }
-
-}
-
-
-class Less_Tree_Mixin_Definition extends Less_Tree_Ruleset
-{
- public $name;
- public $selectors;
- public $params;
- public $arity = 0;
- public $rules;
- public $lookups = array();
- public $required = 0;
- public $frames = array();
- public $condition;
- public $variadic;
- public $type = 'MixinDefinition';
-
-
- // less.js : /lib/less/tree/mixin.js : tree.mixin.Definition
- public function __construct($name, $params, $rules, $condition, $variadic = false, $frames = null)
- {
- $this->name = $name;
- $this->selectors = array(new Less_Tree_Selector(array(new Less_Tree_Element(null, $name))));
-
- $this->params = $params;
- $this->condition = $condition;
- $this->variadic = $variadic;
- $this->rules = $rules;
-
- if ($params) {
- $this->arity = count($params);
- foreach ($params as $p) {
- if (!isset($p['name']) || ($p['name'] && !isset($p['value']))) {
- $this->required++;
- }
- }
- }
-
- $this->frames = $frames;
- $this->SetRulesetIndex();
- }
-
-
-
- //function accept( $visitor ){
- // $this->params = $visitor->visit($this->params);
- // $this->rules = $visitor->visit($this->rules);
- // $this->condition = $visitor->visit($this->condition);
- //}
-
-
- public function toCSS()
- {
- return '';
- }
-
- // less.js : /lib/less/tree/mixin.js : tree.mixin.Definition.evalParams
- public function compileParams($env, $mixinFrames, $args = array(), &$evaldArguments = array())
- {
- $frame = new Less_Tree_Ruleset(null, array());
- $params = $this->params;
- $mixinEnv = null;
- $argsLength = 0;
-
- if ($args) {
- $argsLength = count($args);
- for ($i = 0; $i < $argsLength; $i++) {
- $arg = $args[$i];
-
- if ($arg && $arg['name']) {
- $isNamedFound = false;
-
- foreach ($params as $j => $param) {
- if (!isset($evaldArguments[$j]) && $arg['name'] === $params[$j]['name']) {
- $evaldArguments[$j] = $arg['value']->compile($env);
- array_unshift($frame->rules, new Less_Tree_Rule($arg['name'], $arg['value']->compile($env)));
- $isNamedFound = true;
- break;
- }
- }
- if ($isNamedFound) {
- array_splice($args, $i, 1);
- $i--;
- $argsLength--;
- continue;
- } else {
- throw new Less_Exception_Compiler("Named argument for " . $this->name . ' ' . $args[$i]['name'] . ' not found');
- }
- }
- }
- }
-
- $argIndex = 0;
- foreach ($params as $i => $param) {
-
- if (isset($evaldArguments[$i])) {
- continue;
- }
-
- $arg = null;
- if (isset($args[$argIndex])) {
- $arg = $args[$argIndex];
- }
-
- if (isset($param['name']) && $param['name']) {
-
- if (isset($param['variadic'])) {
- $varargs = array();
- for ($j = $argIndex; $j < $argsLength; $j++) {
- $varargs[] = $args[$j]['value']->compile($env);
- }
- $expression = new Less_Tree_Expression($varargs);
- array_unshift($frame->rules, new Less_Tree_Rule($param['name'], $expression->compile($env)));
- } else {
- $val = ($arg && $arg['value']) ? $arg['value'] : false;
-
- if ($val) {
- $val = $val->compile($env);
- } else if (isset($param['value'])) {
-
- if (!$mixinEnv) {
- $mixinEnv = new Less_Environment();
- $mixinEnv->frames = array_merge(array($frame), $mixinFrames);
- }
-
- $val = $param['value']->compile($mixinEnv);
- $frame->resetCache();
- } else {
- throw new Less_Exception_Compiler("Wrong number of arguments for " . $this->name . " (" . $argsLength . ' for ' . $this->arity . ")");
- }
-
- array_unshift($frame->rules, new Less_Tree_Rule($param['name'], $val));
- $evaldArguments[$i] = $val;
- }
- }
-
- if (isset($param['variadic']) && $args) {
- for ($j = $argIndex; $j < $argsLength; $j++) {
- $evaldArguments[$j] = $args[$j]['value']->compile($env);
- }
- }
- $argIndex++;
- }
-
- ksort($evaldArguments);
- $evaldArguments = array_values($evaldArguments);
-
- return $frame;
- }
-
- public function compile($env)
- {
- if ($this->frames) {
- return new Less_Tree_Mixin_Definition($this->name, $this->params, $this->rules, $this->condition, $this->variadic, $this->frames);
- }
- return new Less_Tree_Mixin_Definition($this->name, $this->params, $this->rules, $this->condition, $this->variadic, $env->frames);
- }
-
- public function evalCall($env, $args = null, $important = null)
- {
-
- Less_Environment::$mixin_stack++;
-
- $_arguments = array();
-
- if ($this->frames) {
- $mixinFrames = array_merge($this->frames, $env->frames);
- } else {
- $mixinFrames = $env->frames;
- }
-
- $frame = $this->compileParams($env, $mixinFrames, $args, $_arguments);
-
- $ex = new Less_Tree_Expression($_arguments);
- array_unshift($frame->rules, new Less_Tree_Rule('@arguments', $ex->compile($env)));
-
-
- $ruleset = new Less_Tree_Ruleset(null, $this->rules);
- $ruleset->originalRuleset = $this->ruleset_id;
-
-
- $ruleSetEnv = new Less_Environment();
- $ruleSetEnv->frames = array_merge(array($this, $frame), $mixinFrames);
- $ruleset = $ruleset->compile($ruleSetEnv);
-
- if ($important) {
- $ruleset = $ruleset->makeImportant();
- }
-
- Less_Environment::$mixin_stack--;
-
- return $ruleset;
- }
-
-
- public function matchCondition($args, $env)
- {
-
- if (!$this->condition) {
- return true;
- }
-
- $frame = $this->compileParams($env, array_merge($this->frames, $env->frames), $args);
-
- $compile_env = new Less_Environment();
- $compile_env->frames = array_merge(
- array($frame) // the parameter variables
- , $this->frames // the parent namespace/mixin frames
- , $env->frames // the current environment frames
- );
-
- return (bool)$this->condition->compile($compile_env);
- }
-
- public function matchArgs($args, $env = null)
- {
- $argsLength = count($args);
-
- if (!$this->variadic) {
- if ($argsLength < $this->required) {
- return false;
- }
- if ($argsLength > count($this->params)) {
- return false;
- }
- } else {
- if ($argsLength < ($this->required - 1)) {
- return false;
- }
- }
-
- $len = min($argsLength, $this->arity);
-
- for ($i = 0; $i < $len; $i++) {
- if (!isset($this->params[$i]['name']) && !isset($this->params[$i]['variadic'])) {
- if ($args[$i]['value']->compile($env)->toCSS() != $this->params[$i]['value']->compile($env)->toCSS()) {
- return false;
- }
- }
- }
-
- return true;
- }
-
-}
-
-
-/**
- * Extend Finder Visitor
- *
- * @package Less
- * @subpackage visitor
- */
-class Less_Visitor_extendFinder extends Less_Visitor
-{
-
- public $contexts = array();
- public $allExtendsStack;
- public $foundExtends;
-
- public function __construct()
- {
- $this->contexts = array();
- $this->allExtendsStack = array(array());
- parent::__construct();
- }
-
- /**
- * @param Less_Tree_Ruleset $root
- */
- public function run($root)
- {
- $root = $this->visitObj($root);
- $root->allExtends =& $this->allExtendsStack[0];
- return $root;
- }
-
- public function visitRule($ruleNode, &$visitDeeper)
- {
- $visitDeeper = false;
- }
-
- public function visitMixinDefinition($mixinDefinitionNode, &$visitDeeper)
- {
- $visitDeeper = false;
- }
-
- public function visitRuleset($rulesetNode)
- {
-
- if ($rulesetNode->root) {
- return;
- }
-
- $allSelectorsExtendList = array();
-
- // get &:extend(.a); rules which apply to all selectors in this ruleset
- if ($rulesetNode->rules) {
- foreach ($rulesetNode->rules as $rule) {
- if ($rule instanceof Less_Tree_Extend) {
- $allSelectorsExtendList[] = $rule;
- $rulesetNode->extendOnEveryPath = true;
- }
- }
- }
-
-
- // now find every selector and apply the extends that apply to all extends
- // and the ones which apply to an individual extend
- foreach ($rulesetNode->paths as $selectorPath) {
- $selector = end($selectorPath); //$selectorPath[ count($selectorPath)-1];
-
- $j = 0;
- foreach ($selector->extendList as $extend) {
- $this->allExtendsStackPush($rulesetNode, $selectorPath, $extend, $j);
- }
- foreach ($allSelectorsExtendList as $extend) {
- $this->allExtendsStackPush($rulesetNode, $selectorPath, $extend, $j);
- }
- }
-
- $this->contexts[] = $rulesetNode->selectors;
- }
-
- public function allExtendsStackPush($rulesetNode, $selectorPath, $extend, &$j)
- {
- $this->foundExtends = true;
- $extend = clone $extend;
- $extend->findSelfSelectors($selectorPath);
- $extend->ruleset = $rulesetNode;
- if ($j === 0) {
- $extend->firstExtendOnThisSelectorPath = true;
- }
-
- $end_key = count($this->allExtendsStack) - 1;
- $this->allExtendsStack[$end_key][] = $extend;
- $j++;
- }
-
-
- public function visitRulesetOut($rulesetNode)
- {
- if (!is_object($rulesetNode) || !$rulesetNode->root) {
- array_pop($this->contexts);
- }
- }
-
- public function visitMedia($mediaNode)
- {
- $mediaNode->allExtends = array();
- $this->allExtendsStack[] =& $mediaNode->allExtends;
- }
-
- public function visitMediaOut()
- {
- array_pop($this->allExtendsStack);
- }
-
- public function visitDirective($directiveNode)
- {
- $directiveNode->allExtends = array();
- $this->allExtendsStack[] =& $directiveNode->allExtends;
- }
-
- public function visitDirectiveOut()
- {
- array_pop($this->allExtendsStack);
- }
-}
-
-
-/*
-class Less_Visitor_import extends Less_VisitorReplacing{
-
- public $_visitor;
- public $_importer;
- public $importCount;
-
- function __construct( $evalEnv ){
- $this->env = $evalEnv;
- $this->importCount = 0;
- parent::__construct();
- }
-
-
- function run( $root ){
- $root = $this->visitObj($root);
- $this->isFinished = true;
-
- //if( $this->importCount === 0) {
- // $this->_finish();
- //}
- }
-
- function visitImport($importNode, &$visitDeeper ){
- $importVisitor = $this;
- $inlineCSS = $importNode->options['inline'];
-
- if( !$importNode->css || $inlineCSS ){
- $evaldImportNode = $importNode->compileForImport($this->env);
-
- if( $evaldImportNode && (!$evaldImportNode->css || $inlineCSS) ){
- $importNode = $evaldImportNode;
- $this->importCount++;
- $env = clone $this->env;
-
- if( (isset($importNode->options['multiple']) && $importNode->options['multiple']) ){
- $env->importMultiple = true;
- }
-
- //get path & uri
- $path_and_uri = null;
- if( is_callable(Less_Parser::$options['import_callback']) ){
- $path_and_uri = call_user_func(Less_Parser::$options['import_callback'],$importNode);
- }
-
- if( !$path_and_uri ){
- $path_and_uri = $importNode->PathAndUri();
- }
-
- if( $path_and_uri ){
- list($full_path, $uri) = $path_and_uri;
- }else{
- $full_path = $uri = $importNode->getPath();
- }
-
-
- //import once
- if( $importNode->skip( $full_path, $env) ){
- return array();
- }
-
- if( $importNode->options['inline'] ){
- //todo needs to reference css file not import
- //$contents = new Less_Tree_Anonymous($importNode->root, 0, array('filename'=>$importNode->importedFilename), true );
-
- Less_Parser::AddParsedFile($full_path);
- $contents = new Less_Tree_Anonymous( file_get_contents($full_path), 0, array(), true );
-
- if( $importNode->features ){
- return new Less_Tree_Media( array($contents), $importNode->features->value );
- }
-
- return array( $contents );
- }
-
-
- // css ?
- if( $importNode->css ){
- $features = ( $importNode->features ? $importNode->features->compile($env) : null );
- return new Less_Tree_Import( $importNode->compilePath( $env), $features, $importNode->options, $this->index);
- }
-
- return $importNode->ParseImport( $full_path, $uri, $env );
- }
-
- }
-
- $visitDeeper = false;
- return $importNode;
- }
-
-
- function visitRule( $ruleNode, &$visitDeeper ){
- $visitDeeper = false;
- return $ruleNode;
- }
-
- function visitDirective($directiveNode, $visitArgs){
- array_unshift($this->env->frames,$directiveNode);
- return $directiveNode;
- }
-
- function visitDirectiveOut($directiveNode) {
- array_shift($this->env->frames);
- }
-
- function visitMixinDefinition($mixinDefinitionNode, $visitArgs) {
- array_unshift($this->env->frames,$mixinDefinitionNode);
- return $mixinDefinitionNode;
- }
-
- function visitMixinDefinitionOut($mixinDefinitionNode) {
- array_shift($this->env->frames);
- }
-
- function visitRuleset($rulesetNode, $visitArgs) {
- array_unshift($this->env->frames,$rulesetNode);
- return $rulesetNode;
- }
-
- function visitRulesetOut($rulesetNode) {
- array_shift($this->env->frames);
- }
-
- function visitMedia($mediaNode, $visitArgs) {
- array_unshift($this->env->frames, $mediaNode->ruleset);
- return $mediaNode;
- }
-
- function visitMediaOut($mediaNode) {
- array_shift($this->env->frames);
- }
-
-}
-*/
-
-
-/**
- * Join Selector Visitor
- *
- * @package Less
- * @subpackage visitor
- */
-class Less_Visitor_joinSelector extends Less_Visitor
-{
-
- public $contexts = array(array());
-
- /**
- * @param Less_Tree_Ruleset $root
- */
- public function run($root)
- {
- return $this->visitObj($root);
- }
-
- public function visitRule($ruleNode, &$visitDeeper)
- {
- $visitDeeper = false;
- }
-
- public function visitMixinDefinition($mixinDefinitionNode, &$visitDeeper)
- {
- $visitDeeper = false;
- }
-
- public function visitRuleset($rulesetNode)
- {
-
- $paths = array();
-
- if (!$rulesetNode->root) {
- $selectors = array();
-
- if ($rulesetNode->selectors && $rulesetNode->selectors) {
- foreach ($rulesetNode->selectors as $selector) {
- if ($selector->getIsOutput()) {
- $selectors[] = $selector;
- }
- }
- }
-
- if (!$selectors) {
- $rulesetNode->selectors = null;
- $rulesetNode->rules = null;
- } else {
- $context = end($this->contexts); //$context = $this->contexts[ count($this->contexts) - 1];
- $paths = $rulesetNode->joinSelectors($context, $selectors);
- }
-
- $rulesetNode->paths = $paths;
- }
-
- $this->contexts[] = $paths; //different from less.js. Placed after joinSelectors() so that $this->contexts will get correct $paths
- }
-
- public function visitRulesetOut()
- {
- array_pop($this->contexts);
- }
-
- public function visitMedia($mediaNode)
- {
- $context = end($this->contexts); //$context = $this->contexts[ count($this->contexts) - 1];
-
- if (!count($context) || (is_object($context[0]) && $context[0]->multiMedia)) {
- $mediaNode->rules[0]->root = true;
- }
- }
-
-}
-
-
-/**
- * Process Extends Visitor
- *
- * @package Less
- * @subpackage visitor
- */
-class Less_Visitor_processExtends extends Less_Visitor
-{
-
- public $allExtendsStack;
-
- /**
- * @param Less_Tree_Ruleset $root
- */
- public function run($root)
- {
- $extendFinder = new Less_Visitor_extendFinder();
- $extendFinder->run($root);
- if (!$extendFinder->foundExtends) {
- return $root;
- }
-
- $root->allExtends = $this->doExtendChaining($root->allExtends, $root->allExtends);
-
- $this->allExtendsStack = array();
- $this->allExtendsStack[] = & $root->allExtends;
-
- return $this->visitObj($root);
- }
-
- private function doExtendChaining($extendsList, $extendsListTarget, $iterationCount = 0)
- {
- //
- // chaining is different from normal extension.. if we extend an extend then we are not just copying, altering and pasting
- // the selector we would do normally, but we are also adding an extend with the same target selector
- // this means this new extend can then go and alter other extends
- //
- // this method deals with all the chaining work - without it, extend is flat and doesn't work on other extend selectors
- // this is also the most expensive.. and a match on one selector can cause an extension of a selector we had already processed if
- // we look at each selector at a time, as is done in visitRuleset
-
- $extendsToAdd = array();
-
-
- //loop through comparing every extend with every target extend.
- // a target extend is the one on the ruleset we are looking at copy/edit/pasting in place
- // e.g. .a:extend(.b) {} and .b:extend(.c) {} then the first extend extends the second one
- // and the second is the target.
- // the seperation into two lists allows us to process a subset of chains with a bigger set, as is the
- // case when processing media queries
- for ($extendIndex = 0, $extendsList_len = count($extendsList); $extendIndex < $extendsList_len; $extendIndex++) {
- for ($targetExtendIndex = 0; $targetExtendIndex < count($extendsListTarget); $targetExtendIndex++) {
-
- $extend = $extendsList[$extendIndex];
- $targetExtend = $extendsListTarget[$targetExtendIndex];
-
- // look for circular references
- if (in_array($targetExtend->object_id, $extend->parent_ids, true)) {
- continue;
- }
-
- // find a match in the target extends self selector (the bit before :extend)
- $selectorPath = array($targetExtend->selfSelectors[0]);
- $matches = $this->findMatch($extend, $selectorPath);
-
-
- if ($matches) {
-
- // we found a match, so for each self selector..
- foreach ($extend->selfSelectors as $selfSelector) {
-
-
- // process the extend as usual
- $newSelector = $this->extendSelector($matches, $selectorPath, $selfSelector);
-
- // but now we create a new extend from it
- $newExtend = new Less_Tree_Extend($targetExtend->selector, $targetExtend->option, 0);
- $newExtend->selfSelectors = $newSelector;
-
- // add the extend onto the list of extends for that selector
- end($newSelector)->extendList = array($newExtend);
- //$newSelector[ count($newSelector)-1]->extendList = array($newExtend);
-
- // record that we need to add it.
- $extendsToAdd[] = $newExtend;
- $newExtend->ruleset = $targetExtend->ruleset;
-
- //remember its parents for circular references
- $newExtend->parent_ids = array_merge($newExtend->parent_ids, $targetExtend->parent_ids, $extend->parent_ids);
-
- // only process the selector once.. if we have :extend(.a,.b) then multiple
- // extends will look at the same selector path, so when extending
- // we know that any others will be duplicates in terms of what is added to the css
- if ($targetExtend->firstExtendOnThisSelectorPath) {
- $newExtend->firstExtendOnThisSelectorPath = true;
- $targetExtend->ruleset->paths[] = $newSelector;
- }
- }
- }
- }
- }
-
- if ($extendsToAdd) {
- // try to detect circular references to stop a stack overflow.
- // may no longer be needed. $this->extendChainCount++;
- if ($iterationCount > 100) {
-
- try {
- $selectorOne = $extendsToAdd[0]->selfSelectors[0]->toCSS();
- $selectorTwo = $extendsToAdd[0]->selector->toCSS();
- } catch (Exception $e) {
- $selectorOne = "{unable to calculate}";
- $selectorTwo = "{unable to calculate}";
- }
-
- throw new Less_Exception_Parser("extend circular reference detected. One of the circular extends is currently:" + $selectorOne + ":extend(" + $selectorTwo + ")");
- }
-
- // now process the new extends on the existing rules so that we can handle a extending b extending c ectending d extending e...
- $extendsToAdd = $this->doExtendChaining($extendsToAdd, $extendsListTarget, $iterationCount + 1);
- }
-
- return array_merge($extendsList, $extendsToAdd);
- }
-
-
- protected function visitRule($ruleNode, &$visitDeeper)
- {
- $visitDeeper = false;
- }
-
- protected function visitMixinDefinition($mixinDefinitionNode, &$visitDeeper)
- {
- $visitDeeper = false;
- }
-
- protected function visitSelector($selectorNode, &$visitDeeper)
- {
- $visitDeeper = false;
- }
-
- protected function visitRuleset($rulesetNode)
- {
-
-
- if ($rulesetNode->root) {
- return;
- }
-
- $allExtends = end($this->allExtendsStack);
- $paths_len = count($rulesetNode->paths);
-
- // look at each selector path in the ruleset, find any extend matches and then copy, find and replace
- foreach ($allExtends as $allExtend) {
- for ($pathIndex = 0; $pathIndex < $paths_len; $pathIndex++) {
-
- // extending extends happens initially, before the main pass
- if (isset($rulesetNode->extendOnEveryPath) && $rulesetNode->extendOnEveryPath) {
- continue;
- }
-
- $selectorPath = $rulesetNode->paths[$pathIndex];
-
- if (end($selectorPath)->extendList) {
- continue;
- }
-
- $this->ExtendMatch($rulesetNode, $allExtend, $selectorPath);
-
- }
- }
- }
-
-
- private function ExtendMatch($rulesetNode, $extend, $selectorPath)
- {
- $matches = $this->findMatch($extend, $selectorPath);
-
- if ($matches) {
- foreach ($extend->selfSelectors as $selfSelector) {
- $rulesetNode->paths[] = $this->extendSelector($matches, $selectorPath, $selfSelector);
- }
- }
- }
-
-
- private function findMatch($extend, $haystackSelectorPath)
- {
-
-
- if (!$this->HasMatches($extend, $haystackSelectorPath)) {
- return false;
- }
-
-
- //
- // look through the haystack selector path to try and find the needle - extend.selector
- // returns an array of selector matches that can then be replaced
- //
- $needleElements = $extend->selector->elements;
- $potentialMatches = array();
- $potentialMatches_len = 0;
- $potentialMatch = null;
- $matches = array();
-
-
- // loop through the haystack elements
- $haystack_path_len = count($haystackSelectorPath);
- for ($haystackSelectorIndex = 0; $haystackSelectorIndex < $haystack_path_len; $haystackSelectorIndex++) {
- $hackstackSelector = $haystackSelectorPath[$haystackSelectorIndex];
-
- $haystack_elements_len = count($hackstackSelector->elements);
- for ($hackstackElementIndex = 0; $hackstackElementIndex < $haystack_elements_len; $hackstackElementIndex++) {
-
- $haystackElement = $hackstackSelector->elements[$hackstackElementIndex];
-
- // if we allow elements before our match we can add a potential match every time. otherwise only at the first element.
- if ($extend->allowBefore || ($haystackSelectorIndex === 0 && $hackstackElementIndex === 0)) {
- $potentialMatches[] = array('pathIndex' => $haystackSelectorIndex, 'index' => $hackstackElementIndex, 'matched' => 0, 'initialCombinator' => $haystackElement->combinator);
- $potentialMatches_len++;
- }
-
- for ($i = 0; $i < $potentialMatches_len; $i++) {
-
- $potentialMatch = & $potentialMatches[$i];
- $potentialMatch = $this->PotentialMatch($potentialMatch, $needleElements, $haystackElement, $hackstackElementIndex);
-
-
- // if we are still valid and have finished, test whether we have elements after and whether these are allowed
- if ($potentialMatch && $potentialMatch['matched'] === $extend->selector->elements_len) {
- $potentialMatch['finished'] = true;
-
- if (!$extend->allowAfter && ($hackstackElementIndex + 1 < $haystack_elements_len || $haystackSelectorIndex + 1 < $haystack_path_len)) {
- $potentialMatch = null;
- }
- }
-
- // if null we remove, if not, we are still valid, so either push as a valid match or continue
- if ($potentialMatch) {
- if ($potentialMatch['finished']) {
- $potentialMatch['length'] = $extend->selector->elements_len;
- $potentialMatch['endPathIndex'] = $haystackSelectorIndex;
- $potentialMatch['endPathElementIndex'] = $hackstackElementIndex + 1; // index after end of match
- $potentialMatches = array(); // we don't allow matches to overlap, so start matching again
- $potentialMatches_len = 0;
- $matches[] = $potentialMatch;
- }
- continue;
- }
-
- array_splice($potentialMatches, $i, 1);
- $potentialMatches_len--;
- $i--;
- }
- }
- }
-
- return $matches;
- }
-
-
- // Before going through all the nested loops, lets check to see if a match is possible
- // Reduces Bootstrap 3.1 compile time from ~6.5s to ~5.6s
- private function HasMatches($extend, $haystackSelectorPath)
- {
-
- if (!$extend->selector->cacheable) {
- return true;
- }
-
- $first_el = $extend->selector->_oelements[0];
-
- foreach ($haystackSelectorPath as $hackstackSelector) {
- if (!$hackstackSelector->cacheable) {
- return true;
- }
-
- if (in_array($first_el, $hackstackSelector->_oelements)) {
- return true;
- }
- }
-
- return false;
- }
-
-
- /**
- * @param integer $hackstackElementIndex
- */
- private function PotentialMatch($potentialMatch, $needleElements, $haystackElement, $hackstackElementIndex)
- {
-
-
- if ($potentialMatch['matched'] > 0) {
-
- // selectors add " " onto the first element. When we use & it joins the selectors together, but if we don't
- // then each selector in haystackSelectorPath has a space before it added in the toCSS phase. so we need to work out
- // what the resulting combinator will be
- $targetCombinator = $haystackElement->combinator;
- if ($targetCombinator === '' && $hackstackElementIndex === 0) {
- $targetCombinator = ' ';
- }
-
- if ($needleElements[$potentialMatch['matched']]->combinator !== $targetCombinator) {
- return null;
- }
- }
-
- // if we don't match, null our match to indicate failure
- if (!$this->isElementValuesEqual($needleElements[$potentialMatch['matched']]->value, $haystackElement->value)) {
- return null;
- }
-
- $potentialMatch['finished'] = false;
- $potentialMatch['matched']++;
-
- return $potentialMatch;
- }
-
-
- private function isElementValuesEqual($elementValue1, $elementValue2)
- {
-
- if ($elementValue1 === $elementValue2) {
- return true;
- }
-
- if (is_string($elementValue1) || is_string($elementValue2)) {
- return false;
- }
-
- if ($elementValue1 instanceof Less_Tree_Attribute) {
- return $this->isAttributeValuesEqual($elementValue1, $elementValue2);
- }
-
- $elementValue1 = $elementValue1->value;
- if ($elementValue1 instanceof Less_Tree_Selector) {
- return $this->isSelectorValuesEqual($elementValue1, $elementValue2);
- }
-
- return false;
- }
-
-
- /**
- * @param Less_Tree_Selector $elementValue1
- */
- private function isSelectorValuesEqual($elementValue1, $elementValue2)
- {
-
- $elementValue2 = $elementValue2->value;
- if (!($elementValue2 instanceof Less_Tree_Selector) || $elementValue1->elements_len !== $elementValue2->elements_len) {
- return false;
- }
-
- for ($i = 0; $i < $elementValue1->elements_len; $i++) {
-
- if ($elementValue1->elements[$i]->combinator !== $elementValue2->elements[$i]->combinator) {
- if ($i !== 0 || ($elementValue1->elements[$i]->combinator || ' ') !== ($elementValue2->elements[$i]->combinator || ' ')) {
- return false;
- }
- }
-
- if (!$this->isElementValuesEqual($elementValue1->elements[$i]->value, $elementValue2->elements[$i]->value)) {
- return false;
- }
- }
-
- return true;
- }
-
-
- /**
- * @param Less_Tree_Attribute $elementValue1
- */
- private function isAttributeValuesEqual($elementValue1, $elementValue2)
- {
-
- if ($elementValue1->op !== $elementValue2->op || $elementValue1->key !== $elementValue2->key) {
- return false;
- }
-
- if (!$elementValue1->value || !$elementValue2->value) {
- if ($elementValue1->value || $elementValue2->value) {
- return false;
- }
- return true;
- }
-
- $elementValue1 = ($elementValue1->value->value ? $elementValue1->value->value : $elementValue1->value);
- $elementValue2 = ($elementValue2->value->value ? $elementValue2->value->value : $elementValue2->value);
-
- return $elementValue1 === $elementValue2;
- }
-
-
- private function extendSelector($matches, $selectorPath, $replacementSelector)
- {
-
- //for a set of matches, replace each match with the replacement selector
-
- $currentSelectorPathIndex = 0;
- $currentSelectorPathElementIndex = 0;
- $path = array();
- $selectorPath_len = count($selectorPath);
-
- for ($matchIndex = 0, $matches_len = count($matches); $matchIndex < $matches_len; $matchIndex++) {
-
-
- $match = $matches[$matchIndex];
- $selector = $selectorPath[$match['pathIndex']];
-
- $firstElement = new Less_Tree_Element(
- $match['initialCombinator'],
- $replacementSelector->elements[0]->value,
- $replacementSelector->elements[0]->index,
- $replacementSelector->elements[0]->currentFileInfo
- );
-
- if ($match['pathIndex'] > $currentSelectorPathIndex && $currentSelectorPathElementIndex > 0) {
- $last_path = end($path);
- $last_path->elements = array_merge($last_path->elements, array_slice($selectorPath[$currentSelectorPathIndex]->elements, $currentSelectorPathElementIndex));
- $currentSelectorPathElementIndex = 0;
- $currentSelectorPathIndex++;
- }
-
- $newElements = array_merge(
- array_slice($selector->elements, $currentSelectorPathElementIndex, ($match['index'] - $currentSelectorPathElementIndex)) // last parameter of array_slice is different than the last parameter of javascript's slice
- , array($firstElement)
- , array_slice($replacementSelector->elements, 1)
- );
-
- if ($currentSelectorPathIndex === $match['pathIndex'] && $matchIndex > 0) {
- $last_key = count($path) - 1;
- $path[$last_key]->elements = array_merge($path[$last_key]->elements, $newElements);
- } else {
- $path = array_merge($path, array_slice($selectorPath, $currentSelectorPathIndex, $match['pathIndex']));
- $path[] = new Less_Tree_Selector($newElements);
- }
-
- $currentSelectorPathIndex = $match['endPathIndex'];
- $currentSelectorPathElementIndex = $match['endPathElementIndex'];
- if ($currentSelectorPathElementIndex >= count($selectorPath[$currentSelectorPathIndex]->elements)) {
- $currentSelectorPathElementIndex = 0;
- $currentSelectorPathIndex++;
- }
- }
-
- if ($currentSelectorPathIndex < $selectorPath_len && $currentSelectorPathElementIndex > 0) {
- $last_path = end($path);
- $last_path->elements = array_merge($last_path->elements, array_slice($selectorPath[$currentSelectorPathIndex]->elements, $currentSelectorPathElementIndex));
- $currentSelectorPathIndex++;
- }
-
- $slice_len = $selectorPath_len - $currentSelectorPathIndex;
- $path = array_merge($path, array_slice($selectorPath, $currentSelectorPathIndex, $slice_len));
-
- return $path;
- }
-
-
- protected function visitMedia($mediaNode)
- {
- $newAllExtends = array_merge($mediaNode->allExtends, end($this->allExtendsStack));
- $this->allExtendsStack[] = $this->doExtendChaining($newAllExtends, $mediaNode->allExtends);
- }
-
- protected function visitMediaOut()
- {
- array_pop($this->allExtendsStack);
- }
-
- protected function visitDirective($directiveNode)
- {
- $newAllExtends = array_merge($directiveNode->allExtends, end($this->allExtendsStack));
- $this->allExtendsStack[] = $this->doExtendChaining($newAllExtends, $directiveNode->allExtends);
- }
-
- protected function visitDirectiveOut()
- {
- array_pop($this->allExtendsStack);
- }
-
-}
-
-/**
- * toCSS Visitor
- *
- * @package Less
- * @subpackage visitor
- */
-class Less_Visitor_toCSS extends Less_VisitorReplacing
-{
-
- private $charset;
-
- public function __construct()
- {
- parent::__construct();
- }
-
- /**
- * @param Less_Tree_Ruleset $root
- */
- public function run($root)
- {
- return $this->visitObj($root);
- }
-
- public function visitRule($ruleNode)
- {
- if ($ruleNode->variable) {
- return array();
- }
- return $ruleNode;
- }
-
- public function visitMixinDefinition($mixinNode)
- {
- // mixin definitions do not get eval'd - this means they keep state
- // so we have to clear that state here so it isn't used if toCSS is called twice
- $mixinNode->frames = array();
- return array();
- }
-
- public function visitExtend()
- {
- return array();
- }
-
- public function visitComment($commentNode)
- {
- if ($commentNode->isSilent()) {
- return array();
- }
- return $commentNode;
- }
-
- public function visitMedia($mediaNode, &$visitDeeper)
- {
- $mediaNode->accept($this);
- $visitDeeper = false;
-
- if (!$mediaNode->rules) {
- return array();
- }
- return $mediaNode;
- }
-
- public function visitDirective($directiveNode)
- {
- if (isset($directiveNode->currentFileInfo['reference']) && (!property_exists($directiveNode, 'isReferenced') || !$directiveNode->isReferenced)) {
- return array();
- }
- if ($directiveNode->name === '@charset') {
- // Only output the debug info together with subsequent @charset definitions
- // a comment (or @media statement) before the actual @charset directive would
- // be considered illegal css as it has to be on the first line
- if (isset($this->charset) && $this->charset) {
-
- //if( $directiveNode->debugInfo ){
- // $comment = new Less_Tree_Comment('/* ' . str_replace("\n",'',$directiveNode->toCSS())." */\n");
- // $comment->debugInfo = $directiveNode->debugInfo;
- // return $this->visit($comment);
- //}
-
-
- return array();
- }
- $this->charset = true;
- }
- return $directiveNode;
- }
-
- public function checkPropertiesInRoot($rulesetNode)
- {
-
- if (!$rulesetNode->firstRoot) {
- return;
- }
-
- foreach ($rulesetNode->rules as $ruleNode) {
- if ($ruleNode instanceof Less_Tree_Rule && !$ruleNode->variable) {
- $msg = "properties must be inside selector blocks, they cannot be in the root. Index " . $ruleNode->index . ($ruleNode->currentFileInfo ? (' Filename: ' . $ruleNode->currentFileInfo['filename']) : null);
- throw new Less_Exception_Compiler($msg);
- }
- }
- }
-
-
- public function visitRuleset($rulesetNode, &$visitDeeper)
- {
-
- $visitDeeper = false;
-
- $this->checkPropertiesInRoot($rulesetNode);
-
- if ($rulesetNode->root) {
- return $this->visitRulesetRoot($rulesetNode);
- }
-
- $rulesets = array();
- $rulesetNode->paths = $this->visitRulesetPaths($rulesetNode);
-
-
- // Compile rules and rulesets
- $nodeRuleCnt = count($rulesetNode->rules);
- for ($i = 0; $i < $nodeRuleCnt;) {
- $rule = $rulesetNode->rules[$i];
-
- if (property_exists($rule, 'rules')) {
- // visit because we are moving them out from being a child
- $rulesets[] = $this->visitObj($rule);
- array_splice($rulesetNode->rules, $i, 1);
- $nodeRuleCnt--;
- continue;
- }
- $i++;
- }
-
-
- // accept the visitor to remove rules and refactor itself
- // then we can decide now whether we want it or not
- if ($nodeRuleCnt > 0) {
- $rulesetNode->accept($this);
-
- if ($rulesetNode->rules) {
-
- if (count($rulesetNode->rules) > 1) {
- $this->_mergeRules($rulesetNode->rules);
- $this->_removeDuplicateRules($rulesetNode->rules);
- }
-
- // now decide whether we keep the ruleset
- if ($rulesetNode->paths) {
- //array_unshift($rulesets, $rulesetNode);
- array_splice($rulesets, 0, 0, array($rulesetNode));
- }
- }
-
- }
-
-
- if (count($rulesets) === 1) {
- return $rulesets[0];
- }
- return $rulesets;
- }
-
-
- /**
- * Helper function for visitiRuleset
- *
- * return array|Less_Tree_Ruleset
- */
- private function visitRulesetRoot($rulesetNode)
- {
- $rulesetNode->accept($this);
- if ($rulesetNode->firstRoot || $rulesetNode->rules) {
- return $rulesetNode;
- }
- return array();
- }
-
-
- /**
- * Helper function for visitRuleset()
- *
- * @return array
- */
- private function visitRulesetPaths($rulesetNode)
- {
-
- $paths = array();
- foreach ($rulesetNode->paths as $p) {
- if ($p[0]->elements[0]->combinator === ' ') {
- $p[0]->elements[0]->combinator = '';
- }
-
- foreach ($p as $pi) {
- if ($pi->getIsReferenced() && $pi->getIsOutput()) {
- $paths[] = $p;
- break;
- }
- }
- }
-
- return $paths;
- }
-
- protected function _removeDuplicateRules(&$rules)
- {
- // remove duplicates
- $ruleCache = array();
- for ($i = count($rules) - 1; $i >= 0; $i--) {
- $rule = $rules[$i];
- if ($rule instanceof Less_Tree_Rule || $rule instanceof Less_Tree_NameValue) {
-
- if (!isset($ruleCache[$rule->name])) {
- $ruleCache[$rule->name] = $rule;
- } else {
- $ruleList =& $ruleCache[$rule->name];
-
- if ($ruleList instanceof Less_Tree_Rule || $ruleList instanceof Less_Tree_NameValue) {
- $ruleList = $ruleCache[$rule->name] = array($ruleCache[$rule->name]->toCSS());
- }
-
- $ruleCSS = $rule->toCSS();
- if (array_search($ruleCSS, $ruleList) !== false) {
- array_splice($rules, $i, 1);
- } else {
- $ruleList[] = $ruleCSS;
- }
- }
- }
- }
- }
-
- protected function _mergeRules(&$rules)
- {
- $groups = array();
-
- //obj($rules);
-
- $rules_len = count($rules);
- for ($i = 0; $i < $rules_len; $i++) {
- $rule = $rules[$i];
-
- if (($rule instanceof Less_Tree_Rule) && $rule->merge) {
-
- $key = $rule->name;
- if ($rule->important) {
- $key .= ',!';
- }
-
- if (!isset($groups[$key])) {
- $groups[$key] = array();
- } else {
- array_splice($rules, $i--, 1);
- $rules_len--;
- }
-
- $groups[$key][] = $rule;
- }
- }
-
-
- foreach ($groups as $parts) {
-
- if (count($parts) > 1) {
- $rule = $parts[0];
- $spacedGroups = array();
- $lastSpacedGroup = array();
- $parts_mapped = array();
- foreach ($parts as $p) {
- if ($p->merge === '+') {
- if ($lastSpacedGroup) {
- $spacedGroups[] = self::toExpression($lastSpacedGroup);
- }
- $lastSpacedGroup = array();
- }
- $lastSpacedGroup[] = $p;
- }
-
- $spacedGroups[] = self::toExpression($lastSpacedGroup);
- $rule->value = self::toValue($spacedGroups);
- }
- }
-
- }
-
- public static function toExpression($values)
- {
- $mapped = array();
- foreach ($values as $p) {
- $mapped[] = $p->value;
- }
- return new Less_Tree_Expression($mapped);
- }
-
- public static function toValue($values)
- {
- //return new Less_Tree_Value($values); ??
-
- $mapped = array();
- foreach ($values as $p) {
- $mapped[] = $p;
- }
- return new Less_Tree_Value($mapped);
- }
-}
-
-
-/**
- * Parser Exception
- *
- * @package Less
- * @subpackage exception
- */
-class Less_Exception_Parser extends Exception
-{
-
- /**
- * The current file
- *
- * @var Less_ImportedFile
- */
- public $currentFile;
-
- /**
- * The current parser index
- *
- * @var integer
- */
- public $index;
-
- protected $input;
-
- protected $details = array();
-
-
- /**
- * Constructor
- *
- * @param string $message
- * @param Exception $previous Previous exception
- * @param integer $index The current parser index
- * @param Less_FileInfo|string $currentFile The file
- * @param integer $code The exception code
- */
- public function __construct($message = null, Exception $previous = null, $index = null, $currentFile = null, $code = 0)
- {
-
- if (PHP_VERSION_ID < 50300) {
- $this->previous = $previous;
- parent::__construct($message, $code);
- } else {
- parent::__construct($message, $code, $previous);
- }
-
- $this->currentFile = $currentFile;
- $this->index = $index;
-
- $this->genMessage();
- }
-
-
- protected function getInput()
- {
-
- if (!$this->input && $this->currentFile && $this->currentFile['filename']) {
- $this->input = file_get_contents($this->currentFile['filename']);
- }
- }
-
-
- /**
- * Converts the exception to string
- *
- * @return string
- */
- public function genMessage()
- {
-
- if ($this->currentFile && $this->currentFile['filename']) {
- $this->message .= ' in ' . basename($this->currentFile['filename']);
- }
-
- if ($this->index !== null) {
- $this->getInput();
- if ($this->input) {
- $line = self::getLineNumber();
- $this->message .= ' on line ' . $line . ', column ' . self::getColumn();
-
- $lines = explode("\n", $this->input);
-
- $count = count($lines);
- $start_line = max(0, $line - 3);
- $last_line = min($count, $start_line + 6);
- $num_len = strlen($last_line);
- for ($i = $start_line; $i < $last_line; $i++) {
- $this->message .= "\n" . str_pad($i + 1, $num_len, '0', STR_PAD_LEFT) . '| ' . $lines[$i];
- }
- }
- }
-
- }
-
- /**
- * Returns the line number the error was encountered
- *
- * @return integer
- */
- public function getLineNumber()
- {
- if ($this->index) {
- return substr_count($this->input, "\n", 0, $this->index) + 1;
- }
- return 1;
- }
-
-
- /**
- * Returns the column the error was encountered
- *
- * @return integer
- */
- public function getColumn()
- {
-
- $part = substr($this->input, 0, $this->index);
- $pos = strrpos($part, "\n");
- return $this->index - $pos;
- }
-
-}
-
-
-/**
- * Chunk Exception
- *
- * @package Less
- * @subpackage exception
- */
-class Less_Exception_Chunk extends Less_Exception_Parser
-{
-
-
- protected $parserCurrentIndex = 0;
-
- protected $emitFrom = 0;
-
- protected $input_len;
-
-
- /**
- * Constructor
- *
- * @param string $input
- * @param Exception $previous Previous exception
- * @param integer $index The current parser index
- * @param Less_FileInfo|string $currentFile The file
- * @param integer $code The exception code
- */
- public function __construct($input, Exception $previous = null, $index = null, $currentFile = null, $code = 0)
- {
-
- $this->message = 'ParseError: Unexpected input'; //default message
-
- $this->index = $index;
-
- $this->currentFile = $currentFile;
-
- $this->input = $input;
- $this->input_len = strlen($input);
-
- $this->Chunks();
- $this->genMessage();
- }
-
-
- /**
- * See less.js chunks()
- * We don't actually need the chunks
- *
- */
- protected function Chunks()
- {
- $level = 0;
- $parenLevel = 0;
- $lastMultiCommentEndBrace = null;
- $lastOpening = null;
- $lastMultiComment = null;
- $lastParen = null;
-
- for ($this->parserCurrentIndex = 0; $this->parserCurrentIndex < $this->input_len; $this->parserCurrentIndex++) {
- $cc = $this->CharCode($this->parserCurrentIndex);
- if ((($cc >= 97) && ($cc <= 122)) || ($cc < 34)) {
- // a-z or whitespace
- continue;
- }
-
- switch ($cc) {
-
- // (
- case 40:
- $parenLevel++;
- $lastParen = $this->parserCurrentIndex;
- continue;
-
- // )
- case 41:
- $parenLevel--;
- if ($parenLevel < 0) {
- return $this->fail("missing opening `(`");
- }
- continue;
-
- // ;
- case 59:
- //if (!$parenLevel) { $this->emitChunk(); }
- continue;
-
- // {
- case 123:
- $level++;
- $lastOpening = $this->parserCurrentIndex;
- continue;
-
- // }
- case 125:
- $level--;
- if ($level < 0) {
- return $this->fail("missing opening `{`");
-
- }
- //if (!$level && !$parenLevel) { $this->emitChunk(); }
- continue;
- // \
- case 92:
- if ($this->parserCurrentIndex < $this->input_len - 1) {
- $this->parserCurrentIndex++;
- continue;
- }
- return $this->fail("unescaped `\\`");
-
- // ", ' and `
- case 34:
- case 39:
- case 96:
- $matched = 0;
- $currentChunkStartIndex = $this->parserCurrentIndex;
- for ($this->parserCurrentIndex = $this->parserCurrentIndex + 1; $this->parserCurrentIndex < $this->input_len; $this->parserCurrentIndex++) {
- $cc2 = $this->CharCode($this->parserCurrentIndex);
- if ($cc2 > 96) {
- continue;
- }
- if ($cc2 == $cc) {
- $matched = 1;
- break;
- }
- if ($cc2 == 92) { // \
- if ($this->parserCurrentIndex == $this->input_len - 1) {
- return $this->fail("unescaped `\\`");
- }
- $this->parserCurrentIndex++;
- }
- }
- if ($matched) {
- continue;
- }
- return $this->fail("unmatched `" + chr($cc) + "`", $currentChunkStartIndex);
-
- // /, check for comment
- case 47:
- if ($parenLevel || ($this->parserCurrentIndex == $this->input_len - 1)) {
- continue;
- }
- $cc2 = $this->CharCode($this->parserCurrentIndex + 1);
- if ($cc2 == 47) {
- // //, find lnfeed
- for ($this->parserCurrentIndex = $this->parserCurrentIndex + 2; $this->parserCurrentIndex < $this->input_len; $this->parserCurrentIndex++) {
- $cc2 = $this->CharCode($this->parserCurrentIndex);
- if (($cc2 <= 13) && (($cc2 == 10) || ($cc2 == 13))) {
- break;
- }
- }
- } else if ($cc2 == 42) {
- // /*, find */
- $lastMultiComment = $currentChunkStartIndex = $this->parserCurrentIndex;
- for ($this->parserCurrentIndex = $this->parserCurrentIndex + 2; $this->parserCurrentIndex < $this->input_len - 1; $this->parserCurrentIndex++) {
- $cc2 = $this->CharCode($this->parserCurrentIndex);
- if ($cc2 == 125) {
- $lastMultiCommentEndBrace = $this->parserCurrentIndex;
- }
- if ($cc2 != 42) {
- continue;
- }
- if ($this->CharCode($this->parserCurrentIndex + 1) == 47) {
- break;
- }
- }
- if ($this->parserCurrentIndex == $this->input_len - 1) {
- return $this->fail("missing closing `*/`", $currentChunkStartIndex);
- }
- }
- continue;
-
- // *, check for unmatched */
- case 42:
- if (($this->parserCurrentIndex < $this->input_len - 1) && ($this->CharCode($this->parserCurrentIndex + 1) == 47)) {
- return $this->fail("unmatched `/*`");
- }
- continue;
- }
- }
-
- if ($level !== 0) {
- if (($lastMultiComment > $lastOpening) && ($lastMultiCommentEndBrace > $lastMultiComment)) {
- return $this->fail("missing closing `}` or `*/`", $lastOpening);
- } else {
- return $this->fail("missing closing `}`", $lastOpening);
- }
- } else if ($parenLevel !== 0) {
- return $this->fail("missing closing `)`", $lastParen);
- }
-
-
- //chunk didn't fail
-
-
- //$this->emitChunk(true);
- }
-
- public function CharCode($pos)
- {
- return ord($this->input[$pos]);
- }
-
-
- public function fail($msg, $index = null)
- {
-
- if (!$index) {
- $this->index = $this->parserCurrentIndex;
- } else {
- $this->index = $index;
- }
- $this->message = 'ParseError: ' . $msg;
- }
-
-
- /*
- function emitChunk( $force = false ){
- $len = $this->parserCurrentIndex - $this->emitFrom;
- if ((($len < 512) && !$force) || !$len) {
- return;
- }
- $chunks[] = substr($this->input, $this->emitFrom, $this->parserCurrentIndex + 1 - $this->emitFrom );
- $this->emitFrom = $this->parserCurrentIndex + 1;
- }
- */
-
-}
-
-
-/**
- * Compiler Exception
- *
- * @package Less
- * @subpackage exception
- */
-class Less_Exception_Compiler extends Less_Exception_Parser
-{
-
-}
-
-/**
- * Parser output with source map
- *
- * @package Less
- * @subpackage Output
- */
-class Less_Output_Mapped extends Less_Output
-{
-
- /**
- * The source map generator
- *
- * @var Less_SourceMap_Generator
- */
- protected $generator;
-
- /**
- * Current line
- *
- * @var integer
- */
- protected $lineNumber = 0;
-
- /**
- * Current column
- *
- * @var integer
- */
- protected $column = 0;
-
- /**
- * Array of contents map (file and its content)
- *
- * @var array
- */
- protected $contentsMap = array();
-
- /**
- * Constructor
- *
- * @param array $contentsMap Array of filename to contents map
- * @param Less_SourceMap_Generator $generator
- */
- public function __construct(array $contentsMap, $generator)
- {
- $this->contentsMap = $contentsMap;
- $this->generator = $generator;
- }
-
- /**
- * Adds a chunk to the stack
- * The $index for less.php may be different from less.js since less.php does not chunkify inputs
- *
- * @param string $chunk
- * @param string $fileInfo
- * @param integer $index
- * @param mixed $mapLines
- */
- public function add($chunk, $fileInfo = null, $index = 0, $mapLines = null)
- {
-
- //ignore adding empty strings
- if ($chunk === '') {
- return;
- }
-
-
- $sourceLines = array();
- $sourceColumns = ' ';
-
-
- if ($fileInfo) {
-
- $url = $fileInfo['currentUri'];
-
- if (isset($this->contentsMap[$url])) {
- $inputSource = substr($this->contentsMap[$url], 0, $index);
- $sourceLines = explode("\n", $inputSource);
- $sourceColumns = end($sourceLines);
- } else {
- throw new Exception('Filename ' . $url . ' not in contentsMap');
- }
-
- }
-
- $lines = explode("\n", $chunk);
- $columns = end($lines);
-
- if ($fileInfo) {
-
- if (!$mapLines) {
- $this->generator->addMapping(
- $this->lineNumber + 1, // generated_line
- $this->column, // generated_column
- count($sourceLines), // original_line
- strlen($sourceColumns), // original_column
- $fileInfo
- );
- } else {
- for ($i = 0, $count = count($lines); $i < $count; $i++) {
- $this->generator->addMapping(
- $this->lineNumber + $i + 1, // generated_line
- $i === 0 ? $this->column : 0, // generated_column
- count($sourceLines) + $i, // original_line
- $i === 0 ? strlen($sourceColumns) : 0, // original_column
- $fileInfo
- );
- }
- }
- }
-
- if (count($lines) === 1) {
- $this->column += strlen($columns);
- } else {
- $this->lineNumber += count($lines) - 1;
- $this->column = strlen($columns);
- }
-
- // add only chunk
- parent::add($chunk);
- }
-
-}
-
-/**
- * Encode / Decode Base64 VLQ.
- *
- * @package Less
- * @subpackage SourceMap
- */
-class Less_SourceMap_Base64VLQ
-{
-
- /**
- * Shift
- *
- * @var integer
- */
- private $shift = 5;
-
- /**
- * Mask
- *
- * @var integer
- */
- private $mask = 0x1F; // == (1 << shift) == 0b00011111
-
- /**
- * Continuation bit
- *
- * @var integer
- */
- private $continuationBit = 0x20; // == (mask - 1 ) == 0b00100000
-
- /**
- * Char to integer map
- *
- * @var array
- */
- private $charToIntMap = array(
- 'A' => 0, 'B' => 1, 'C' => 2, 'D' => 3, 'E' => 4, 'F' => 5, 'G' => 6,
- 'H' => 7, 'I' => 8, 'J' => 9, 'K' => 10, 'L' => 11, 'M' => 12, 'N' => 13,
- 'O' => 14, 'P' => 15, 'Q' => 16, 'R' => 17, 'S' => 18, 'T' => 19, 'U' => 20,
- 'V' => 21, 'W' => 22, 'X' => 23, 'Y' => 24, 'Z' => 25, 'a' => 26, 'b' => 27,
- 'c' => 28, 'd' => 29, 'e' => 30, 'f' => 31, 'g' => 32, 'h' => 33, 'i' => 34,
- 'j' => 35, 'k' => 36, 'l' => 37, 'm' => 38, 'n' => 39, 'o' => 40, 'p' => 41,
- 'q' => 42, 'r' => 43, 's' => 44, 't' => 45, 'u' => 46, 'v' => 47, 'w' => 48,
- 'x' => 49, 'y' => 50, 'z' => 51, 0 => 52, 1 => 53, 2 => 54, 3 => 55, 4 => 56,
- 5 => 57, 6 => 58, 7 => 59, 8 => 60, 9 => 61, '+' => 62, '/' => 63,
- );
-
- /**
- * Integer to char map
- *
- * @var array
- */
- private $intToCharMap = array(
- 0 => 'A', 1 => 'B', 2 => 'C', 3 => 'D', 4 => 'E', 5 => 'F', 6 => 'G',
- 7 => 'H', 8 => 'I', 9 => 'J', 10 => 'K', 11 => 'L', 12 => 'M', 13 => 'N',
- 14 => 'O', 15 => 'P', 16 => 'Q', 17 => 'R', 18 => 'S', 19 => 'T', 20 => 'U',
- 21 => 'V', 22 => 'W', 23 => 'X', 24 => 'Y', 25 => 'Z', 26 => 'a', 27 => 'b',
- 28 => 'c', 29 => 'd', 30 => 'e', 31 => 'f', 32 => 'g', 33 => 'h', 34 => 'i',
- 35 => 'j', 36 => 'k', 37 => 'l', 38 => 'm', 39 => 'n', 40 => 'o', 41 => 'p',
- 42 => 'q', 43 => 'r', 44 => 's', 45 => 't', 46 => 'u', 47 => 'v', 48 => 'w',
- 49 => 'x', 50 => 'y', 51 => 'z', 52 => '0', 53 => '1', 54 => '2', 55 => '3',
- 56 => '4', 57 => '5', 58 => '6', 59 => '7', 60 => '8', 61 => '9', 62 => '+',
- 63 => '/',
- );
-
- /**
- * Constructor
- */
- public function __construct()
- {
- // I leave it here for future reference
- // foreach(str_split('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/') as $i => $char)
- // {
- // $this->charToIntMap[$char] = $i;
- // $this->intToCharMap[$i] = $char;
- // }
- }
-
- /**
- * Convert from a two-complement value to a value where the sign bit is
- * is placed in the least significant bit. For example, as decimals:
- * 1 becomes 2 (10 binary), -1 becomes 3 (11 binary)
- * 2 becomes 4 (100 binary), -2 becomes 5 (101 binary)
- * We generate the value for 32 bit machines, hence -2147483648 becomes 1, not 4294967297,
- * even on a 64 bit machine.
- * @param string $aValue
- */
- public function toVLQSigned($aValue)
- {
- return 0xffffffff & ($aValue < 0 ? ((-$aValue) << 1) + 1 : ($aValue << 1) + 0);
- }
-
- /**
- * Convert to a two-complement value from a value where the sign bit is
- * is placed in the least significant bit. For example, as decimals:
- * 2 (10 binary) becomes 1, 3 (11 binary) becomes -1
- * 4 (100 binary) becomes 2, 5 (101 binary) becomes -2
- * We assume that the value was generated with a 32 bit machine in mind.
- * Hence
- * 1 becomes -2147483648
- * even on a 64 bit machine.
- * @param integer $aValue
- */
- public function fromVLQSigned($aValue)
- {
- return $aValue & 1 ? $this->zeroFill(~$aValue + 2, 1) | (-1 - 0x7fffffff) : $this->zeroFill($aValue, 1);
- }
-
- /**
- * Return the base 64 VLQ encoded value.
- *
- * @param string $aValue The value to encode
- * @return string The encoded value
- */
- public function encode($aValue)
- {
- $encoded = '';
- $vlq = $this->toVLQSigned($aValue);
- do {
- $digit = $vlq & $this->mask;
- $vlq = $this->zeroFill($vlq, $this->shift);
- if ($vlq > 0) {
- $digit |= $this->continuationBit;
- }
- $encoded .= $this->base64Encode($digit);
- } while ($vlq > 0);
-
- return $encoded;
- }
-
- /**
- * Return the value decoded from base 64 VLQ.
- *
- * @param string $encoded The encoded value to decode
- * @return integer The decoded value
- */
- public function decode($encoded)
- {
- $vlq = 0;
- $i = 0;
- do {
- $digit = $this->base64Decode($encoded[$i]);
- $vlq |= ($digit & $this->mask) << ($i * $this->shift);
- $i++;
- } while ($digit & $this->continuationBit);
-
- return $this->fromVLQSigned($vlq);
- }
-
- /**
- * Right shift with zero fill.
- *
- * @param integer $a number to shift
- * @param integer $b number of bits to shift
- * @return integer
- */
- public function zeroFill($a, $b)
- {
- return ($a >= 0) ? ($a >> $b) : ($a >> $b) & (PHP_INT_MAX >> ($b - 1));
- }
-
- /**
- * Encode single 6-bit digit as base64.
- *
- * @param integer $number
- * @return string
- * @throws Exception If the number is invalid
- */
- public function base64Encode($number)
- {
- if ($number < 0 || $number > 63) {
- throw new Exception(sprintf('Invalid number "%s" given. Must be between 0 and 63.', $number));
- }
- return $this->intToCharMap[$number];
- }
-
- /**
- * Decode single 6-bit digit from base64
- *
- * @param string $char
- * @return number
- * @throws Exception If the number is invalid
- */
- public function base64Decode($char)
- {
- if (!array_key_exists($char, $this->charToIntMap)) {
- throw new Exception(sprintf('Invalid base 64 digit "%s" given.', $char));
- }
- return $this->charToIntMap[$char];
- }
-
-}
-
-
-/**
- * Source map generator
- *
- * @package Less
- * @subpackage Output
- */
-class Less_SourceMap_Generator extends Less_Configurable
-{
-
- /**
- * What version of source map does the generator generate?
- */
- const VERSION = 3;
-
- /**
- * Array of default options
- *
- * @var array
- */
- protected $defaultOptions = array(
- // an optional source root, useful for relocating source files
- // on a server or removing repeated values in the 'sources' entry.
- // This value is prepended to the individual entries in the 'source' field.
- 'sourceRoot' => '',
-
- // an optional name of the generated code that this source map is associated with.
- 'sourceMapFilename' => null,
-
- // url of the map
- 'sourceMapURL' => null,
-
- // absolute path to a file to write the map to
- 'sourceMapWriteTo' => null,
-
- // output source contents?
- 'outputSourceFiles' => false,
-
- // base path for filename normalization
- 'sourceMapRootpath' => '',
-
- // base path for filename normalization
- 'sourceMapBasepath' => ''
- );
-
- /**
- * The base64 VLQ encoder
- *
- * @var Less_SourceMap_Base64VLQ
- */
- protected $encoder;
-
- /**
- * Array of mappings
- *
- * @var array
- */
- protected $mappings = array();
-
- /**
- * The root node
- *
- * @var Less_Tree_Ruleset
- */
- protected $root;
-
- /**
- * Array of contents map
- *
- * @var array
- */
- protected $contentsMap = array();
-
- /**
- * File to content map
- *
- * @var array
- */
- protected $sources = array();
- protected $source_keys = array();
-
- /**
- * Constructor
- *
- * @param Less_Tree_Ruleset $root The root node
- * @param array $options Array of options
- */
- public function __construct(Less_Tree_Ruleset $root, $contentsMap, $options = array())
- {
- $this->root = $root;
- $this->contentsMap = $contentsMap;
- $this->encoder = new Less_SourceMap_Base64VLQ();
-
- $this->SetOptions($options);
-
-
- // fix windows paths
- if (!empty($this->options['sourceMapRootpath'])) {
- $this->options['sourceMapRootpath'] = str_replace('\\', '/', $this->options['sourceMapRootpath']);
- $this->options['sourceMapRootpath'] = rtrim($this->options['sourceMapRootpath'], '/') . '/';
- }
- }
-
- /**
- * Generates the CSS
- *
- * @return string
- */
- public function generateCSS()
- {
- $output = new Less_Output_Mapped($this->contentsMap, $this);
-
- // catch the output
- $this->root->genCSS($output);
-
-
- $sourceMapUrl = $this->getOption('sourceMapURL');
- $sourceMapFilename = $this->getOption('sourceMapFilename');
- $sourceMapContent = $this->generateJson();
- $sourceMapWriteTo = $this->getOption('sourceMapWriteTo');
-
- if (!$sourceMapUrl && $sourceMapFilename) {
- $sourceMapUrl = $this->normalizeFilename($sourceMapFilename);
- }
-
- // write map to a file
- if ($sourceMapWriteTo) {
- $this->saveMap($sourceMapWriteTo, $sourceMapContent);
- }
-
- // inline the map
- if (!$sourceMapUrl) {
- $sourceMapUrl = sprintf('data:application/json,%s', Less_Functions::encodeURIComponent($sourceMapContent));
- }
-
- if ($sourceMapUrl) {
- $output->add(sprintf('/*# sourceMappingURL=%s */', $sourceMapUrl));
- }
-
- return $output->toString();
- }
-
- /**
- * Saves the source map to a file
- *
- * @param string $file The absolute path to a file
- * @param string $content The content to write
- * @throws Exception If the file could not be saved
- */
- protected function saveMap($file, $content)
- {
- $dir = dirname($file);
- // directory does not exist
- if (!is_dir($dir)) {
- // FIXME: create the dir automatically?
- throw new Exception(sprintf('The directory "%s" does not exist. Cannot save the source map.', $dir));
- }
- // FIXME: proper saving, with dir write check!
- if (file_put_contents($file, $content) === false) {
- throw new Exception(sprintf('Cannot save the source map to "%s"', $file));
- }
- return true;
- }
-
- /**
- * Normalizes the filename
- *
- * @param string $filename
- * @return string
- */
- protected function normalizeFilename($filename)
- {
-
- $filename = str_replace('\\', '/', $filename);
- $rootpath = $this->getOption('sourceMapRootpath');
- $basePath = $this->getOption('sourceMapBasepath');
-
- // "Trim" the 'sourceMapBasepath' from the output filename.
- if (strpos($filename, $basePath) === 0) {
- $filename = substr($filename, strlen($basePath));
- }
-
- // Remove extra leading path separators.
- if (strpos($filename, '\\') === 0 || strpos($filename, '/') === 0) {
- $filename = substr($filename, 1);
- }
-
- return $rootpath . $filename;
- }
-
- /**
- * Adds a mapping
- *
- * @param integer $generatedLine The line number in generated file
- * @param integer $generatedColumn The column number in generated file
- * @param integer $originalLine The line number in original file
- * @param integer $originalColumn The column number in original file
- * @param string $sourceFile The original source file
- */
- public function addMapping($generatedLine, $generatedColumn, $originalLine, $originalColumn, $fileInfo)
- {
-
- $this->mappings[] = array(
- 'generated_line' => $generatedLine,
- 'generated_column' => $generatedColumn,
- 'original_line' => $originalLine,
- 'original_column' => $originalColumn,
- 'source_file' => $fileInfo['currentUri']
- );
-
- $this->sources[$fileInfo['currentUri']] = $fileInfo['filename'];
- }
-
-
- /**
- * Generates the JSON source map
- *
- * @return string
- * @see https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#
- */
- protected function generateJson()
- {
-
- $sourceMap = array();
- $mappings = $this->generateMappings();
-
- // File version (always the first entry in the object) and must be a positive integer.
- $sourceMap['version'] = self::VERSION;
-
-
- // An optional name of the generated code that this source map is associated with.
- $file = $this->getOption('sourceMapFilename');
- if ($file) {
- $sourceMap['file'] = $file;
- }
-
-
- // An optional source root, useful for relocating source files on a server or removing repeated values in the 'sources' entry. This value is prepended to the individual entries in the 'source' field.
- $root = $this->getOption('sourceRoot');
- if ($root) {
- $sourceMap['sourceRoot'] = $root;
- }
-
-
- // A list of original sources used by the 'mappings' entry.
- $sourceMap['sources'] = array();
- foreach ($this->sources as $source_uri => $source_filename) {
- $sourceMap['sources'][] = $this->normalizeFilename($source_filename);
- }
-
-
- // A list of symbol names used by the 'mappings' entry.
- $sourceMap['names'] = array();
-
- // A string with the encoded mapping data.
- $sourceMap['mappings'] = $mappings;
-
- if ($this->getOption('outputSourceFiles')) {
- // An optional list of source content, useful when the 'source' can't be hosted.
- // The contents are listed in the same order as the sources above.
- // 'null' may be used if some original sources should be retrieved by name.
- $sourceMap['sourcesContent'] = $this->getSourcesContent();
- }
-
- // less.js compat fixes
- if (count($sourceMap['sources']) && empty($sourceMap['sourceRoot'])) {
- unset($sourceMap['sourceRoot']);
- }
-
- return json_encode($sourceMap);
- }
-
- /**
- * Returns the sources contents
- *
- * @return array|null
- */
- protected function getSourcesContent()
- {
- if (empty($this->sources)) {
- return;
- }
- $content = array();
- foreach ($this->sources as $sourceFile) {
- $content[] = file_get_contents($sourceFile);
- }
- return $content;
- }
-
- /**
- * Generates the mappings string
- *
- * @return string
- */
- public function generateMappings()
- {
-
- if (!count($this->mappings)) {
- return '';
- }
-
- $this->source_keys = array_flip(array_keys($this->sources));
-
-
- // group mappings by generated line number.
- $groupedMap = $groupedMapEncoded = array();
- foreach ($this->mappings as $m) {
- $groupedMap[$m['generated_line']][] = $m;
- }
- ksort($groupedMap);
-
- $lastGeneratedLine = $lastOriginalIndex = $lastOriginalLine = $lastOriginalColumn = 0;
-
- foreach ($groupedMap as $lineNumber => $line_map) {
- while (++$lastGeneratedLine < $lineNumber) {
- $groupedMapEncoded[] = ';';
- }
-
- $lineMapEncoded = array();
- $lastGeneratedColumn = 0;
-
- foreach ($line_map as $m) {
- $mapEncoded = $this->encoder->encode($m['generated_column'] - $lastGeneratedColumn);
- $lastGeneratedColumn = $m['generated_column'];
-
- // find the index
- if ($m['source_file']) {
- $index = $this->findFileIndex($m['source_file']);
- if ($index !== false) {
- $mapEncoded .= $this->encoder->encode($index - $lastOriginalIndex);
- $lastOriginalIndex = $index;
-
- // lines are stored 0-based in SourceMap spec version 3
- $mapEncoded .= $this->encoder->encode($m['original_line'] - 1 - $lastOriginalLine);
- $lastOriginalLine = $m['original_line'] - 1;
-
- $mapEncoded .= $this->encoder->encode($m['original_column'] - $lastOriginalColumn);
- $lastOriginalColumn = $m['original_column'];
- }
- }
-
- $lineMapEncoded[] = $mapEncoded;
- }
-
- $groupedMapEncoded[] = implode(',', $lineMapEncoded) . ';';
- }
-
- return rtrim(implode($groupedMapEncoded), ';');
- }
-
- /**
- * Finds the index for the filename
- *
- * @param string $filename
- * @return integer|false
- */
- protected function findFileIndex($filename)
- {
- return $this->source_keys[$filename];
- }
-
-}
\ No newline at end of file
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/.gitattributes b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/.gitattributes
new file mode 100644
index 000000000..8a678cab1
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/.gitattributes
@@ -0,0 +1,3 @@
+test/ export-ignore
+.travis.yml export-ignore
+phpunit.xml export-ignore
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/.gitignore b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/.gitignore
new file mode 100644
index 000000000..8734cb159
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/.gitignore
@@ -0,0 +1,14 @@
+.buildpath
+.project
+.settings
+.idea
+.DS_Store?
+ehthumbs.db
+Icon?
+Thumbs.db
+*.komodoproject
+composer.lock
+vendor/
+x_*
+X_*
+_*
\ No newline at end of file
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/CHANGES.md b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/CHANGES.md
new file mode 100644
index 000000000..a798eb0d1
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/CHANGES.md
@@ -0,0 +1,42 @@
+# 1.8.1
+- [All Changes](https://github.com/Asenar/less.php/compare/v1.8.0...v1.8.1)
+- Another PHP 7.3 compatibility tweak
+
+# 1.8.0
+- [All Changes](https://github.com/Asenar/less.php/compare/v1.7.0.13...v1.8.0)
+- Wikimedia fork
+- Supports up to PHP 7.3
+- No longer tested against PHP 5, though it's still remains allowed in `composer.json` for HHVM compatibility
+- Switched to [semantic versioning](https://semver.org/), hence version numbers now use 3 digits
+
+# 1.7.0.13
+ - [All Changes](https://github.com/Asenar/less.php/compare/v1.7.0.12...v1.7.0.13)
+ - Fix composer.json (PSR-4 was invalid)
+
+# 1.7.0.12
+ - [All Changes](https://github.com/Asenar/less.php/compare/v1.7.0.11...v1.7.0.12)
+ - set bin/lessc bit executable
+ - Add 'gettingVariables' method in Less_Parser
+
+# 1.7.0.11
+ - [All Changes](https://github.com/Asenar/less.php/compare/v1.7.0.10...v1.7.0.11)
+ - Fix realpath issue (windows)
+ - Set Less_Tree_Call property back to public ( Fix 258 266 267 issues from oyejorge/less.php)
+
+# 1.7.0.10
+
+ - [All Changes](https://github.com/oyejorge/less.php/compare/v1.7.0.9...v1.7.10)
+ - Add indentation option
+ - Add 'optional' modifier for @import
+ - fix $color in Exception messages
+ - don't use set_time_limit when running cli
+ - take relative-url into account when building the cache filename
+ - urlArgs should be string no array()
+ - add bug-report fixtures [#6dc898f](https://github.com/oyejorge/less.php/commit/6dc898f5d75b447464906bdf19d79c2e19d95e33)
+ - fix #269, missing on NameValue type [#a8dac63](https://github.com/oyejorge/less.php/commit/a8dac63d93fb941c54fb78b12588abf635747c1b)
+
+# 1.7.0.9
+
+ - [All Changes](https://github.com/oyejorge/less.php/compare/v1.7.0.8...v1.7.0.9)
+ - Remove space at beginning of Version.php
+ - Revert require() paths in test interface
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/LICENSE b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/LICENSE
new file mode 100644
index 000000000..82216a5da
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/LICENSE
@@ -0,0 +1,178 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/README.md b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/README.md
new file mode 100644
index 000000000..cca5f57e3
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/README.md
@@ -0,0 +1,337 @@
+[Less.php](http://lessphp.typesettercms.com)
+========
+
+This is the Wikimedia fork of a PHP port of the official LESS processor . [![Build Status](https://travis-ci.org/wikimedia/less.php.png?branch=master)](https://travis-ci.org/wikimedia/less.php)
+
+* [About](#about)
+* [Installation](#installation)
+* [Basic Use](#basic-use)
+* [Caching](#caching)
+* [Source Maps](#source-maps)
+* [Command Line](#command-line)
+* [Integration with other projects](#integration-with-other-projects)
+* [Transitioning from Leafo/lessphp](#transitioning-from-leafolessphp)
+* [Credits](#credits)
+
+
+
+About
+---
+The code structure of less.php mirrors that of the official processor which helps us ensure compatibility and allows for easy maintenance.
+
+Please note, there are a few unsupported LESS features:
+
+- Evaluation of JavaScript expressions within back-ticks (for obvious reasons).
+- Definition of custom functions.
+
+
+Installation
+---
+
+You can install the library with Composer or manually.
+
+#### Composer
+
+Step 1. Edit your `composer.json`:
+
+```json
+{
+ "require": {
+ "wikimedia/less.php": "~1.7.0.9"
+ }
+}
+```
+
+Step 2. Install it:
+
+```bash
+$ curl -sS https://getcomposer.org/installer | php
+$ php composer.phar install
+```
+
+#### Manually From Release
+
+Step 1. [Download the latest release](https://github.com/wikimedia/less.php/releases) and upload the PHP files to your server.
+
+Step 2. Include the library:
+
+```php
+require_once '[path to less.php]/Less.php';
+```
+
+#### Manually From Source
+
+Step 1. [Download the source](https://github.com/wikimedia/less.php/archive/master.zip) and upload the files in /lib/Less to a folder on your server.
+
+Step 2. Include the library and register the autoloader
+
+```php
+require_once '[path to less.php]/Autoloader.php';
+Less_Autoloader::register();
+```
+
+Basic Use
+---
+
+#### Parsing Strings
+
+```php
+$parser = new Less_Parser();
+$parser->parse( '@color: #4D926F; #header { color: @color; } h2 { color: @color; }' );
+$css = $parser->getCss();
+```
+
+
+#### Parsing LESS Files
+The parseFile() function takes two arguments:
+
+1. The absolute path of the .less file to be parsed
+2. The url root to prepend to any relative image or @import urls in the .less file.
+
+```php
+$parser = new Less_Parser();
+$parser->parseFile( '/var/www/mysite/bootstrap.less', 'http://example.com/mysite/' );
+$css = $parser->getCss();
+```
+
+
+#### Handling Invalid LESS
+An exception will be thrown if the compiler encounters invalid LESS.
+
+```php
+try{
+ $parser = new Less_Parser();
+ $parser->parseFile( '/var/www/mysite/bootstrap.less', 'http://example.com/mysite/' );
+ $css = $parser->getCss();
+}catch(Exception $e){
+ $error_message = $e->getMessage();
+}
+```
+
+
+#### Parsing Multiple Sources
+less.php can parse multiple sources to generate a single CSS file.
+
+```php
+$parser = new Less_Parser();
+$parser->parseFile( '/var/www/mysite/bootstrap.less', '/mysite/' );
+$parser->parse( '@color: #4D926F; #header { color: @color; } h2 { color: @color; }' );
+$css = $parser->getCss();
+```
+
+#### Getting Info About The Parsed Files
+less.php can tell you which .less files were imported and parsed.
+
+```php
+$parser = new Less_Parser();
+$parser->parseFile( '/var/www/mysite/bootstrap.less', '/mysite/' );
+$css = $parser->getCss();
+$imported_files = $parser->allParsedFiles();
+```
+
+
+#### Compressing Output
+You can tell less.php to remove comments and whitespace to generate minimized CSS files.
+
+```php
+$options = array( 'compress'=>true );
+$parser = new Less_Parser( $options );
+$parser->parseFile( '/var/www/mysite/bootstrap.less', '/mysite/' );
+$css = $parser->getCss();
+```
+
+#### Getting Variables
+You can use the getVariables() method to get an all variables defined and
+their value in a php associative array. Note that LESS has to be previously
+compiled.
+```php
+$parser = new Less_Parser;
+$parser->parseFile( '/var/www/mysite/bootstrap.less');
+$css = $parser->getCss();
+$variables = $parser->getVariables();
+
+```
+
+
+
+#### Setting Variables
+You can use the ModifyVars() method to customize your CSS if you have variables stored in PHP associative arrays.
+
+```php
+$parser = new Less_Parser();
+$parser->parseFile( '/var/www/mysite/bootstrap.less', '/mysite/' );
+$parser->ModifyVars( array('font-size-base'=>'16px') );
+$css = $parser->getCss();
+```
+
+
+#### Import Directories
+By default, less.php will look for @imports in the directory of the file passed to parseFile().
+If you're using parse() or if @imports reside in different directories, you can tell less.php where to look.
+
+```php
+$directories = array( '/var/www/mysite/bootstrap/' => '/mysite/bootstrap/' );
+$parser = new Less_Parser();
+$parser->SetImportDirs( $directories );
+$parser->parseFile( '/var/www/mysite/theme.less', '/mysite/' );
+$css = $parser->getCss();
+```
+
+
+Caching
+---
+Compiling LESS code into CSS is a time consuming process, caching your results is highly recommended.
+
+
+#### Caching CSS
+Use the Less_Cache class to save and reuse the results of compiled LESS files.
+This method will check the modified time and size of each LESS file (including imported files) and regenerate a new CSS file when changes are found.
+Note: When changes are found, this method will return a different file name for the new cached content.
+
+```php
+$less_files = array( '/var/www/mysite/bootstrap.less' => '/mysite/' );
+$options = array( 'cache_dir' => '/var/www/writable_folder' );
+$css_file_name = Less_Cache::Get( $less_files, $options );
+$compiled = file_get_contents( '/var/www/writable_folder/'.$css_file_name );
+```
+
+#### Caching CSS With Variables
+Passing options to Less_Cache::Get()
+
+```php
+$less_files = array( '/var/www/mysite/bootstrap.less' => '/mysite/' );
+$options = array( 'cache_dir' => '/var/www/writable_folder' );
+$variables = array( 'width' => '100px' );
+$css_file_name = Less_Cache::Get( $less_files, $options, $variables );
+$compiled = file_get_contents( '/var/www/writable_folder/'.$css_file_name );
+```
+
+
+#### Parser Caching
+less.php will save serialized parser data for each .less file if a writable folder is passed to the SetCacheDir() method.
+Note: This feature only caches intermediate parsing results to improve the performance of repeated CSS generation.
+Your application should cache any CSS generated by less.php.
+
+```php
+$options = array('cache_dir'=>'/var/www/writable_folder');
+$parser = new Less_Parser( $options );
+$parser->parseFile( '/var/www/mysite/bootstrap.less', '/mysite/' );
+$css = $parser->getCss();
+```
+
+You can specify the caching technique used by changing the ```cache_method``` option. Supported methods are:
+* ```php```: Creates valid PHP files which can be included without any changes (default method).
+* ```var_export```: Like "php", but using PHP's ```var_export()``` function without any optimizations.
+ It's recommended to use "php" instead.
+* ```serialize```: Faster, but pretty memory-intense.
+* ```callback```: Use custom callback functions to implement your own caching method. Give the "cache_callback_get" and
+ "cache_callback_set" options with callables (see PHP's ```call_user_func()``` and ```is_callable()``` functions). less.php
+ will pass the parser object (class ```Less_Parser```), the path to the parsed .less file ("/some/path/to/file.less") and
+ an identifier that will change every time the .less file is modified. The ```get``` callback must return the ruleset
+ (an array with ```Less_Tree``` objects) provided as fourth parameter of the ```set``` callback. If something goes wrong,
+ return ```NULL``` (cache doesn't exist) or ```FALSE```.
+
+
+
+Source Maps
+---
+Less.php supports v3 sourcemaps
+
+#### Inline
+The sourcemap will be appended to the generated CSS file.
+
+```php
+$options = array( 'sourceMap' => true );
+$parser = new Less_Parser($options);
+$parser->parseFile( '/var/www/mysite/bootstrap.less', '/mysite/' );
+$css = $parser->getCss();
+```
+
+#### Saving to Map File
+
+```php
+$options = array(
+ 'sourceMap' => true,
+ 'sourceMapWriteTo' => '/var/www/mysite/writable_folder/filename.map',
+ 'sourceMapURL' => '/mysite/writable_folder/filename.map',
+ );
+$parser = new Less_Parser($options);
+$parser->parseFile( '/var/www/mysite/bootstrap.less', '/mysite/' );
+$css = $parser->getCss();
+```
+
+
+Command line
+---
+An additional script has been included to use the compiler from the command line.
+In the simplest invocation, you specify an input file and the compiled CSS is written to standard out:
+
+```
+$ lessc input.less > output.css
+```
+
+By using the -w flag you can watch a specified input file and have it compile as needed to the output file:
+
+```
+$ lessc -w input.less output.css
+```
+
+Errors from watch mode are written to standard out.
+
+For more help, run `lessc --help`
+
+
+Integration with other projects
+---
+
+#### Drupal 7
+
+This library can be used as drop-in replacement of lessphp to work with [Drupal 7 less module](https://drupal.org/project/less).
+
+How to install:
+
+1. [Download the less.php source code](https://github.com/wikimedia/less.php/archive/master.zip) and unzip it so that 'lessc.inc.php' is located at 'sites/all/libraries/lessphp/lessc.inc.php'.
+2. Download and install [Drupal 7 less module](https://drupal.org/project/less) as usual.
+3. That's it :)
+
+#### JBST WordPress theme
+
+JBST has a built-in LESS compiler based on lessphp. Customize your WordPress theme with LESS.
+
+How to use / install:
+
+1. [Download the latest release](https://github.com/bassjobsen/jamedo-bootstrap-start-theme) copy the files to your {wordpress/}wp-content/themes folder and activate it.
+2. Find the compiler under Appearance > LESS Compiler in your WordPress dashboard
+3. Enter your LESS code in the text area and press (re)compile
+
+Use the built-in compiler to:
+- set any [Bootstrap](http://getbootstrap.com/customize/) variable or use Bootstrap's mixins:
+ -`@navbar-default-color: blue;`
+ - create a custom button: `.btn-custom {
+ .button-variant(white; red; blue);
+}`
+- set any built-in LESS variable: for example `@footer_bg_color: black;` sets the background color of the footer to black
+- use built-in mixins: - add a custom font: `.include-custom-font(@family: arial,@font-path, @path: @custom-font-dir, @weight: normal, @style: normal);`
+
+The compiler can also be downloaded as [plugin](http://wordpress.org/plugins/wp-less-to-css/)
+
+#### WordPress
+
+This simple plugin will simply make the library available to other plugins and themes and can be used as a dependency using the [TGM Library](http://tgmpluginactivation.com/)
+
+How to install:
+
+1. Install the plugin from your WordPress Dashboard: http://wordpress.org/plugins/lessphp/
+2. That's it :)
+
+
+Transitioning from Leafo/lessphp
+---
+Projects looking for an easy transition from leafo/lessphp can use the lessc.inc.php adapter. To use, [Download the less.php source code](https://github.com/wikimedia/less.php/archive/master.zip) and unzip the files into your project so that the new 'lessc.inc.php' replaces the existing 'lessc.inc.php'.
+
+Note, the 'setPreserveComments' will no longer have any effect on the compiled LESS.
+
+Credits
+---
+less.php was originally ported to PHP by [Matt Agar](https://github.com/agar) and then updated by [Martin Jantošovič](https://github.com/Mordred). This Wikimedia-maintained fork was split off from [Josh Schmidt's version](https://github.com/oyejorge/less.php).
+
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/bin/lessc b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/bin/lessc
new file mode 100644
index 000000000..fa1fb9588
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/bin/lessc
@@ -0,0 +1,191 @@
+#!/usr/bin/env php
+ false, 'relativeUrls' => false);
+$silent = false;
+$watch = false;
+$rootpath = '';
+
+// Check for arguments
+array_shift($argv);
+if (!count($argv)) {
+ $argv[] = '-h';
+}
+
+// parse arguments
+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 'h':
+ case 'help':
+ echo << 1) {
+ $output = array_pop($argv);
+ $inputs = $argv;
+}
+else {
+ $inputs = $argv;
+ $output = false;
+}
+
+if (!count($inputs)) {
+ echo("lessc: no input files\n");
+ exit;
+}
+
+if ($watch) {
+ if (!$output) {
+ echo("lessc: you must specify the output file if --watch is given\n");
+ exit;
+ }
+
+ $lastAction = 0;
+
+ echo("lessc: watching input files\n");
+
+ while (1) {
+ clearstatcache();
+
+ $updated = false;
+ foreach ($inputs as $input) {
+ if ($input == '-') {
+ if (count($inputs) == 1) {
+ echo("lessc: during watching files is not possible to watch stdin\n");
+ exit;
+ }
+ else {
+ continue;
+ }
+ }
+
+ if (filemtime($input) > $lastAction) {
+ $updated = true;
+ break;
+ }
+ }
+
+ if ($updated) {
+ $lastAction = time();
+ $parser = new Less_Parser($env);
+ foreach ($inputs as $input) {
+ try {
+ $parser->parseFile($input, $rootpath);
+ }
+ catch (Exception $e) {
+ echo("lessc: " . $e->getMessage() . " \n");
+ continue; // Invalid processing
+ }
+ }
+
+ file_put_contents($output, $parser->getCss());
+ echo("lessc: output file recompiled\n");
+ }
+
+ sleep(1);
+ }
+}
+else {
+ $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: " . ((string)$e) . " \n");
+ }
+ }
+ }
+ }
+
+ if ($output) {
+ file_put_contents($output, $parser->getCss());
+ }
+ else {
+ echo $parser->getCss();
+ }
+}
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/composer.json b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/composer.json
new file mode 100644
index 000000000..2ad73ebc3
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/composer.json
@@ -0,0 +1,38 @@
+{
+ "name": "wikimedia/less.php",
+ "description": "PHP port of the Javascript version of LESS http://lesscss.org (Originally maintained by Josh Schmidt)",
+ "keywords": [ "less", "css", "php", "stylesheet", "less.js", "lesscss" ],
+ "license": "Apache-2.0",
+ "authors": [
+ {
+ "name": "Josh Schmidt",
+ "homepage": "https://github.com/oyejorge"
+ },
+ {
+ "name": "Matt Agar",
+ "homepage": "https://github.com/agar"
+ },
+ {
+ "name": "Martin Jantošovič",
+ "homepage": "https://github.com/Mordred"
+ }
+ ],
+ "require": {
+ "PHP" : ">=5.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.8.24"
+ },
+ "scripts": {
+ "test": [
+ "phpunit"
+ ]
+ },
+ "autoload": {
+ "psr-0": { "Less": "lib/" },
+ "classmap": ["lessc.inc.php"]
+ },
+ "bin": [
+ "bin/lessc"
+ ]
+}
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lessc.inc.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lessc.inc.php
new file mode 100644
index 000000000..e6cdf20ab
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lessc.inc.php
@@ -0,0 +1,276 @@
+importDir = (array)$dirs;
+ }
+
+ public function addImportDir( $dir ) {
+ $this->importDir = (array)$this->importDir;
+ $this->importDir[] = $dir;
+ }
+
+ public function setFormatter( $name ) {
+ $this->formatterName = $name;
+ }
+
+ public function setPreserveComments( $preserve ) {}
+
+ public function registerFunction( $name, $func ) {
+ $this->libFunctions[$name] = $func;
+ }
+
+ public function unregisterFunction( $name ) {
+ unset( $this->libFunctions[$name] );
+ }
+
+ public function setVariables( $variables ){
+ foreach ( $variables as $name => $value ) {
+ $this->setVariable( $name, $value );
+ }
+ }
+
+ public function setVariable( $name, $value ) {
+ $this->registeredVars[$name] = $value;
+ }
+
+ public function unsetVariable( $name ) {
+ unset( $this->registeredVars[$name] );
+ }
+
+ public function setOptions( $options ) {
+ foreach ( $options as $name => $value ) {
+ $this->setOption( $name, $value);
+ }
+ }
+
+ public function setOption( $name, $value ) {
+ $this->options[$name] = $value;
+ }
+
+ public function parse( $buffer, $presets = array() ) {
+
+ $this->setVariables( $presets );
+
+ $parser = new Less_Parser( $this->getOptions() );
+ $parser->setImportDirs( $this->getImportDirs() );
+ foreach ( $this->libFunctions as $name => $func ) {
+ $parser->registerFunction( $name, $func );
+ }
+ $parser->parse($buffer);
+ if ( count( $this->registeredVars ) ) {
+ $parser->ModifyVars( $this->registeredVars );
+ }
+
+ return $parser->getCss();
+ }
+
+ protected function getOptions() {
+ $options = array( 'relativeUrls'=>false );
+ switch( $this->formatterName ) {
+ case 'compressed':
+ $options['compress'] = true;
+ break;
+ }
+ if (is_array($this->options))
+ {
+ $options = array_merge($options, $this->options);
+ }
+ return $options;
+ }
+
+ protected function getImportDirs() {
+ $dirs_ = (array)$this->importDir;
+ $dirs = array();
+ foreach ( $dirs_ as $dir ) {
+ $dirs[$dir] = '';
+ }
+ return $dirs;
+ }
+
+ public function compile( $string, $name = null ) {
+
+ $oldImport = $this->importDir;
+ $this->importDir = (array)$this->importDir;
+
+ $this->allParsedFiles = array();
+
+ $parser = new Less_Parser( $this->getOptions() );
+ $parser->SetImportDirs( $this->getImportDirs() );
+ if ( count( $this->registeredVars ) ) {
+ $parser->ModifyVars( $this->registeredVars );
+ }
+ foreach ( $this->libFunctions as $name => $func ) {
+ $parser->registerFunction( $name, $func );
+ }
+ $parser->parse( $string );
+ $out = $parser->getCss();
+
+ $parsed = Less_Parser::AllParsedFiles();
+ foreach ( $parsed as $file ) {
+ $this->addParsedFile( $file );
+ }
+
+ $this->importDir = $oldImport;
+
+ return $out;
+ }
+
+ public function compileFile( $fname, $outFname = null ) {
+ if ( !is_readable( $fname ) ) {
+ throw new Exception( 'load error: failed to find '.$fname );
+ }
+
+ $pi = pathinfo( $fname );
+
+ $oldImport = $this->importDir;
+
+ $this->importDir = (array)$this->importDir;
+ $this->importDir[] = Less_Parser::AbsPath( $pi['dirname'] ).'/';
+
+ $this->allParsedFiles = array();
+ $this->addParsedFile( $fname );
+
+ $parser = new Less_Parser( $this->getOptions() );
+ $parser->SetImportDirs( $this->getImportDirs() );
+ if ( count( $this->registeredVars ) ) {
+ $parser->ModifyVars( $this->registeredVars );
+ }
+ foreach ( $this->libFunctions as $name => $func ) {
+ $parser->registerFunction( $name, $func );
+ }
+ $parser->parseFile( $fname );
+ $out = $parser->getCss();
+
+ $parsed = Less_Parser::AllParsedFiles();
+ foreach ( $parsed as $file ) {
+ $this->addParsedFile( $file );
+ }
+
+ $this->importDir = $oldImport;
+
+ if ( $outFname !== null ) {
+ return file_put_contents( $outFname, $out );
+ }
+
+ return $out;
+ }
+
+ public function checkedCompile( $in, $out ) {
+ if ( !is_file( $out ) || filemtime( $in ) > filemtime( $out ) ) {
+ $this->compileFile($in, $out);
+ return true;
+ }
+ return false;
+ }
+
+
+ /**
+ * Execute lessphp on a .less file or a lessphp cache structure
+ *
+ * The lessphp cache structure contains information about a specific
+ * less file having been parsed. It can be used as a hint for future
+ * calls to determine whether or not a rebuild is required.
+ *
+ * The cache structure contains two important keys that may be used
+ * externally:
+ *
+ * compiled: The final compiled CSS
+ * updated: The time (in seconds) the CSS was last compiled
+ *
+ * The cache structure is a plain-ol' PHP associative array and can
+ * be serialized and unserialized without a hitch.
+ *
+ * @param mixed $in Input
+ * @param bool $force Force rebuild?
+ * @return array lessphp cache structure
+ */
+ public function cachedCompile( $in, $force = false ) {
+ // assume no root
+ $root = null;
+
+ if ( is_string( $in ) ) {
+ $root = $in;
+ } elseif ( is_array( $in ) and isset( $in['root'] ) ) {
+ if ( $force or ! isset( $in['files'] ) ) {
+ // If we are forcing a recompile or if for some reason the
+ // structure does not contain any file information we should
+ // specify the root to trigger a rebuild.
+ $root = $in['root'];
+ } elseif ( isset( $in['files'] ) and is_array( $in['files'] ) ) {
+ foreach ( $in['files'] as $fname => $ftime ) {
+ if ( !file_exists( $fname ) or filemtime( $fname ) > $ftime ) {
+ // One of the files we knew about previously has changed
+ // so we should look at our incoming root again.
+ $root = $in['root'];
+ break;
+ }
+ }
+ }
+ } else {
+ // TODO: Throw an exception? We got neither a string nor something
+ // that looks like a compatible lessphp cache structure.
+ return null;
+ }
+
+ if ( $root !== null ) {
+ // If we have a root value which means we should rebuild.
+ $out = array();
+ $out['root'] = $root;
+ $out['compiled'] = $this->compileFile($root);
+ $out['files'] = $this->allParsedFiles();
+ $out['updated'] = time();
+ return $out;
+ } else {
+ // No changes, pass back the structure
+ // we were given initially.
+ return $in;
+ }
+ }
+
+ public function ccompile( $in, $out, $less = null ) {
+ if ( $less === null ) {
+ $less = new self;
+ }
+ return $less->checkedCompile( $in, $out );
+ }
+
+ public static function cexecute( $in, $force = false, $less = null ) {
+ if ( $less === null ) {
+ $less = new self;
+ }
+ return $less->cachedCompile($in, $force);
+ }
+
+ public function allParsedFiles() {
+ return $this->allParsedFiles;
+ }
+
+ protected function addParsedFile( $file ) {
+ $this->allParsedFiles[Less_Parser::AbsPath( $file )] = filemtime( $file );
+ }
+}
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/.easymin/ignore_prefixes b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/.easymin/ignore_prefixes
new file mode 100644
index 000000000..ca953b29b
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/.easymin/ignore_prefixes
@@ -0,0 +1,2 @@
+.easymin
+Autoloader.php
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Autoloader.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Autoloader.php
new file mode 100644
index 000000000..b6300c020
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Autoloader.php
@@ -0,0 +1,79 @@
+ '/');
+ }
+
+
+ // generate name for compiled css file
+ $hash = md5(json_encode($less_files));
+ $list_file = Less_Cache::$cache_dir . Less_Cache::$prefix . $hash . '.list';
+
+ // check cached content
+ if( !isset($parser_options['use_cache']) || $parser_options['use_cache'] === true ){
+ if( file_exists($list_file) ){
+
+ self::ListFiles($list_file, $list, $cached_name);
+ $compiled_name = self::CompiledName($list, $hash);
+
+ // if $cached_name is the same as the $compiled name, don't regenerate
+ if( !$cached_name || $cached_name === $compiled_name ){
+
+ $output_file = self::OutputFile($compiled_name, $parser_options );
+
+ if( $output_file && file_exists($output_file) ){
+ @touch($list_file);
+ return basename($output_file); // for backwards compatibility, we just return the name of the file
+ }
+ }
+ }
+ }
+
+ $compiled = self::Cache( $less_files, $parser_options );
+ if( !$compiled ){
+ return false;
+ }
+
+ $compiled_name = self::CompiledName( $less_files, $hash );
+ $output_file = self::OutputFile($compiled_name, $parser_options );
+
+
+ //save the file list
+ $list = $less_files;
+ $list[] = $compiled_name;
+ $cache = implode("\n",$list);
+ file_put_contents( $list_file, $cache );
+
+
+ //save the css
+ file_put_contents( $output_file, $compiled );
+
+
+ //clean up
+ self::CleanCache();
+
+ return basename($output_file);
+ }
+
+ /**
+ * Force the compiler to regenerate the cached css file
+ *
+ * @param array $less_files Array of .less files to compile
+ * @param array $parser_options Array of compiler options
+ * @param array $modify_vars Array of variables
+ * @return string Name of the css file
+ */
+ public static function Regen( $less_files, $parser_options = array(), $modify_vars = array() ){
+ $parser_options['use_cache'] = false;
+ return self::Get( $less_files, $parser_options, $modify_vars );
+ }
+
+ public static function Cache( &$less_files, $parser_options = array() ){
+
+
+ // get less.php if it exists
+ $file = dirname(__FILE__) . '/Less.php';
+ if( file_exists($file) && !class_exists('Less_Parser') ){
+ require_once($file);
+ }
+
+ $parser_options['cache_dir'] = Less_Cache::$cache_dir;
+ $parser = new Less_Parser($parser_options);
+
+
+ // combine files
+ foreach($less_files as $file_path => $uri_or_less ){
+
+ //treat as less markup if there are newline characters
+ if( strpos($uri_or_less,"\n") !== false ){
+ $parser->Parse( $uri_or_less );
+ continue;
+ }
+
+ $parser->ParseFile( $file_path, $uri_or_less );
+ }
+
+ $compiled = $parser->getCss();
+
+
+ $less_files = $parser->allParsedFiles();
+
+ return $compiled;
+ }
+
+
+ private static function OutputFile( $compiled_name, $parser_options ){
+
+ //custom output file
+ if( !empty($parser_options['output']) ){
+
+ //relative to cache directory?
+ if( preg_match('#[\\\\/]#',$parser_options['output']) ){
+ return $parser_options['output'];
+ }
+
+ return Less_Cache::$cache_dir.$parser_options['output'];
+ }
+
+ return Less_Cache::$cache_dir.$compiled_name;
+ }
+
+
+ private static function CompiledName( $files, $extrahash ){
+
+ //save the file list
+ $temp = array(Less_Version::cache_version);
+ foreach($files as $file){
+ $temp[] = filemtime($file)."\t".filesize($file)."\t".$file;
+ }
+
+ return Less_Cache::$prefix.sha1(json_encode($temp).$extrahash).'.css';
+ }
+
+
+ public static function SetCacheDir( $dir ){
+ Less_Cache::$cache_dir = $dir;
+ self::CheckCacheDir();
+ }
+
+ public static function CheckCacheDir(){
+
+ Less_Cache::$cache_dir = str_replace('\\','/',Less_Cache::$cache_dir);
+ Less_Cache::$cache_dir = rtrim(Less_Cache::$cache_dir,'/').'/';
+
+ if( !file_exists(Less_Cache::$cache_dir) ){
+ if( !mkdir(Less_Cache::$cache_dir) ){
+ throw new Less_Exception_Parser('Less.php cache directory couldn\'t be created: '.Less_Cache::$cache_dir);
+ }
+
+ }elseif( !is_dir(Less_Cache::$cache_dir) ){
+ throw new Less_Exception_Parser('Less.php cache directory doesn\'t exist: '.Less_Cache::$cache_dir);
+
+ }elseif( !is_writable(Less_Cache::$cache_dir) ){
+ throw new Less_Exception_Parser('Less.php cache directory isn\'t writable: '.Less_Cache::$cache_dir);
+
+ }
+
+ }
+
+
+ /**
+ * Delete unused less.php files
+ *
+ */
+ public static function CleanCache(){
+ static $clean = false;
+
+
+ if( $clean || empty(Less_Cache::$cache_dir) ){
+ return;
+ }
+
+ $clean = true;
+
+ // only remove files with extensions created by less.php
+ // css files removed based on the list files
+ $remove_types = array('lesscache'=>1,'list'=>1,'less'=>1,'map'=>1);
+
+ $files = scandir(Less_Cache::$cache_dir);
+ if( !$files ){
+ return;
+ }
+
+ $check_time = time() - self::$gc_lifetime;
+ foreach($files as $file){
+
+
+ // don't delete if the file wasn't created with less.php
+ if( strpos($file,Less_Cache::$prefix) !== 0 ){
+ continue;
+ }
+
+ $parts = explode('.',$file);
+ $type = array_pop($parts);
+
+
+ if( !isset($remove_types[$type]) ){
+ continue;
+ }
+
+ $full_path = Less_Cache::$cache_dir . $file;
+ $mtime = filemtime($full_path);
+
+ // don't delete if it's a relatively new file
+ if( $mtime > $check_time ){
+ continue;
+ }
+
+
+ // delete the list file and associated css file
+ if( $type === 'list' ){
+ self::ListFiles($full_path, $list, $css_file_name);
+ if( $css_file_name ){
+ $css_file = Less_Cache::$cache_dir . $css_file_name;
+ if( file_exists($css_file) ){
+ unlink($css_file);
+ }
+ }
+ }
+
+ unlink($full_path);
+ }
+
+
+ }
+
+
+ /**
+ * Get the list of less files and generated css file from a list file
+ *
+ */
+ static function ListFiles($list_file, &$list, &$css_file_name ){
+
+ $list = explode("\n",file_get_contents($list_file));
+
+ //pop the cached name that should match $compiled_name
+ $css_file_name = array_pop($list);
+
+ if( !preg_match('/^' . Less_Cache::$prefix . '[a-f0-9]+\.css$/',$css_file_name) ){
+ $list[] = $css_file_name;
+ $css_file_name = false;
+ }
+
+ }
+
+}
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Colors.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Colors.php
new file mode 100644
index 000000000..ad3f31dc4
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Colors.php
@@ -0,0 +1,170 @@
+'#f0f8ff',
+ 'antiquewhite'=>'#faebd7',
+ 'aqua'=>'#00ffff',
+ 'aquamarine'=>'#7fffd4',
+ 'azure'=>'#f0ffff',
+ 'beige'=>'#f5f5dc',
+ 'bisque'=>'#ffe4c4',
+ 'black'=>'#000000',
+ 'blanchedalmond'=>'#ffebcd',
+ 'blue'=>'#0000ff',
+ 'blueviolet'=>'#8a2be2',
+ 'brown'=>'#a52a2a',
+ 'burlywood'=>'#deb887',
+ 'cadetblue'=>'#5f9ea0',
+ 'chartreuse'=>'#7fff00',
+ 'chocolate'=>'#d2691e',
+ 'coral'=>'#ff7f50',
+ 'cornflowerblue'=>'#6495ed',
+ 'cornsilk'=>'#fff8dc',
+ 'crimson'=>'#dc143c',
+ 'cyan'=>'#00ffff',
+ 'darkblue'=>'#00008b',
+ 'darkcyan'=>'#008b8b',
+ 'darkgoldenrod'=>'#b8860b',
+ 'darkgray'=>'#a9a9a9',
+ 'darkgrey'=>'#a9a9a9',
+ 'darkgreen'=>'#006400',
+ 'darkkhaki'=>'#bdb76b',
+ 'darkmagenta'=>'#8b008b',
+ 'darkolivegreen'=>'#556b2f',
+ 'darkorange'=>'#ff8c00',
+ 'darkorchid'=>'#9932cc',
+ 'darkred'=>'#8b0000',
+ 'darksalmon'=>'#e9967a',
+ 'darkseagreen'=>'#8fbc8f',
+ 'darkslateblue'=>'#483d8b',
+ 'darkslategray'=>'#2f4f4f',
+ 'darkslategrey'=>'#2f4f4f',
+ 'darkturquoise'=>'#00ced1',
+ 'darkviolet'=>'#9400d3',
+ 'deeppink'=>'#ff1493',
+ 'deepskyblue'=>'#00bfff',
+ 'dimgray'=>'#696969',
+ 'dimgrey'=>'#696969',
+ 'dodgerblue'=>'#1e90ff',
+ 'firebrick'=>'#b22222',
+ 'floralwhite'=>'#fffaf0',
+ 'forestgreen'=>'#228b22',
+ 'fuchsia'=>'#ff00ff',
+ 'gainsboro'=>'#dcdcdc',
+ 'ghostwhite'=>'#f8f8ff',
+ 'gold'=>'#ffd700',
+ 'goldenrod'=>'#daa520',
+ 'gray'=>'#808080',
+ 'grey'=>'#808080',
+ 'green'=>'#008000',
+ 'greenyellow'=>'#adff2f',
+ 'honeydew'=>'#f0fff0',
+ 'hotpink'=>'#ff69b4',
+ 'indianred'=>'#cd5c5c',
+ 'indigo'=>'#4b0082',
+ 'ivory'=>'#fffff0',
+ 'khaki'=>'#f0e68c',
+ 'lavender'=>'#e6e6fa',
+ 'lavenderblush'=>'#fff0f5',
+ 'lawngreen'=>'#7cfc00',
+ 'lemonchiffon'=>'#fffacd',
+ 'lightblue'=>'#add8e6',
+ 'lightcoral'=>'#f08080',
+ 'lightcyan'=>'#e0ffff',
+ 'lightgoldenrodyellow'=>'#fafad2',
+ 'lightgray'=>'#d3d3d3',
+ 'lightgrey'=>'#d3d3d3',
+ 'lightgreen'=>'#90ee90',
+ 'lightpink'=>'#ffb6c1',
+ 'lightsalmon'=>'#ffa07a',
+ 'lightseagreen'=>'#20b2aa',
+ 'lightskyblue'=>'#87cefa',
+ 'lightslategray'=>'#778899',
+ 'lightslategrey'=>'#778899',
+ 'lightsteelblue'=>'#b0c4de',
+ 'lightyellow'=>'#ffffe0',
+ 'lime'=>'#00ff00',
+ 'limegreen'=>'#32cd32',
+ 'linen'=>'#faf0e6',
+ 'magenta'=>'#ff00ff',
+ 'maroon'=>'#800000',
+ 'mediumaquamarine'=>'#66cdaa',
+ 'mediumblue'=>'#0000cd',
+ 'mediumorchid'=>'#ba55d3',
+ 'mediumpurple'=>'#9370d8',
+ 'mediumseagreen'=>'#3cb371',
+ 'mediumslateblue'=>'#7b68ee',
+ 'mediumspringgreen'=>'#00fa9a',
+ 'mediumturquoise'=>'#48d1cc',
+ 'mediumvioletred'=>'#c71585',
+ 'midnightblue'=>'#191970',
+ 'mintcream'=>'#f5fffa',
+ 'mistyrose'=>'#ffe4e1',
+ 'moccasin'=>'#ffe4b5',
+ 'navajowhite'=>'#ffdead',
+ 'navy'=>'#000080',
+ 'oldlace'=>'#fdf5e6',
+ 'olive'=>'#808000',
+ 'olivedrab'=>'#6b8e23',
+ 'orange'=>'#ffa500',
+ 'orangered'=>'#ff4500',
+ 'orchid'=>'#da70d6',
+ 'palegoldenrod'=>'#eee8aa',
+ 'palegreen'=>'#98fb98',
+ 'paleturquoise'=>'#afeeee',
+ 'palevioletred'=>'#d87093',
+ 'papayawhip'=>'#ffefd5',
+ 'peachpuff'=>'#ffdab9',
+ 'peru'=>'#cd853f',
+ 'pink'=>'#ffc0cb',
+ 'plum'=>'#dda0dd',
+ 'powderblue'=>'#b0e0e6',
+ 'purple'=>'#800080',
+ 'red'=>'#ff0000',
+ 'rosybrown'=>'#bc8f8f',
+ 'royalblue'=>'#4169e1',
+ 'saddlebrown'=>'#8b4513',
+ 'salmon'=>'#fa8072',
+ 'sandybrown'=>'#f4a460',
+ 'seagreen'=>'#2e8b57',
+ 'seashell'=>'#fff5ee',
+ 'sienna'=>'#a0522d',
+ 'silver'=>'#c0c0c0',
+ 'skyblue'=>'#87ceeb',
+ 'slateblue'=>'#6a5acd',
+ 'slategray'=>'#708090',
+ 'slategrey'=>'#708090',
+ 'snow'=>'#fffafa',
+ 'springgreen'=>'#00ff7f',
+ 'steelblue'=>'#4682b4',
+ 'tan'=>'#d2b48c',
+ 'teal'=>'#008080',
+ 'thistle'=>'#d8bfd8',
+ 'tomato'=>'#ff6347',
+ 'turquoise'=>'#40e0d0',
+ 'violet'=>'#ee82ee',
+ 'wheat'=>'#f5deb3',
+ 'white'=>'#ffffff',
+ 'whitesmoke'=>'#f5f5f5',
+ 'yellow'=>'#ffff00',
+ 'yellowgreen'=>'#9acd32'
+ );
+
+ public static function hasOwnProperty($color) {
+ return isset(self::$colors[$color]);
+ }
+
+
+ public static function color($color) {
+ return self::$colors[$color];
+ }
+
+}
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Configurable.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Configurable.php
new file mode 100644
index 000000000..aa7fd3eb7
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Configurable.php
@@ -0,0 +1,69 @@
+defaultOptions);
+ $this->options = array_merge($this->defaultOptions, $this->options, $options);
+ }
+
+
+ /**
+ * Get an option value by name
+ *
+ * If the option is empty or not set a NULL value will be returned.
+ *
+ * @param string $name
+ * @param mixed $default Default value if confiuration of $name is not present
+ * @return mixed
+ */
+ public function getOption($name, $default = null){
+ if(isset($this->options[$name])){
+ return $this->options[$name];
+ }
+ return $default;
+ }
+
+
+ /**
+ * Set an option
+ *
+ * @param string $name
+ * @param mixed $value
+ */
+ public function setOption($name, $value){
+ $this->options[$name] = $value;
+ }
+
+}
\ No newline at end of file
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Environment.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Environment.php
new file mode 100644
index 000000000..b2203014f
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Environment.php
@@ -0,0 +1,166 @@
+ ',',
+ ': ' => ':',
+ '' => '',
+ ' ' => ' ',
+ ':' => ' :',
+ '+' => '+',
+ '~' => '~',
+ '>' => '>',
+ '|' => '|',
+ '^' => '^',
+ '^^' => '^^'
+ );
+
+ }else{
+
+ Less_Environment::$_outputMap = array(
+ ',' => ', ',
+ ': ' => ': ',
+ '' => '',
+ ' ' => ' ',
+ ':' => ' :',
+ '+' => ' + ',
+ '~' => ' ~ ',
+ '>' => ' > ',
+ '|' => '|',
+ '^' => ' ^ ',
+ '^^' => ' ^^ '
+ );
+
+ }
+ }
+
+
+ public function copyEvalEnv($frames = array() ){
+ $new_env = new Less_Environment();
+ $new_env->frames = $frames;
+ return $new_env;
+ }
+
+
+ public static function isMathOn(){
+ return !Less_Parser::$options['strictMath'] || Less_Environment::$parensStack;
+ }
+
+ public static function isPathRelative($path){
+ return !preg_match('/^(?:[a-z-]+:|\/)/',$path);
+ }
+
+
+ /**
+ * Canonicalize a path by resolving references to '/./', '/../'
+ * Does not remove leading "../"
+ * @param string path or url
+ * @return string Canonicalized path
+ *
+ */
+ public static function normalizePath($path){
+
+ $segments = explode('/',$path);
+ $segments = array_reverse($segments);
+
+ $path = array();
+ $path_len = 0;
+
+ while( $segments ){
+ $segment = array_pop($segments);
+ switch( $segment ) {
+
+ case '.':
+ break;
+
+ case '..':
+ if( !$path_len || ( $path[$path_len-1] === '..') ){
+ $path[] = $segment;
+ $path_len++;
+ }else{
+ array_pop($path);
+ $path_len--;
+ }
+ break;
+
+ default:
+ $path[] = $segment;
+ $path_len++;
+ break;
+ }
+ }
+
+ return implode('/',$path);
+ }
+
+
+ public function unshiftFrame($frame){
+ array_unshift($this->frames, $frame);
+ }
+
+ public function shiftFrame(){
+ return array_shift($this->frames);
+ }
+
+}
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Exception/Chunk.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Exception/Chunk.php
new file mode 100644
index 000000000..0a4f4e507
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Exception/Chunk.php
@@ -0,0 +1,203 @@
+message = 'ParseError: Unexpected input'; //default message
+
+ $this->index = $index;
+
+ $this->currentFile = $currentFile;
+
+ $this->input = $input;
+ $this->input_len = strlen($input);
+
+ $this->Chunks();
+ $this->genMessage();
+ }
+
+
+ /**
+ * See less.js chunks()
+ * We don't actually need the chunks
+ *
+ */
+ protected function Chunks(){
+ $level = 0;
+ $parenLevel = 0;
+ $lastMultiCommentEndBrace = null;
+ $lastOpening = null;
+ $lastMultiComment = null;
+ $lastParen = null;
+
+ for( $this->parserCurrentIndex = 0; $this->parserCurrentIndex < $this->input_len; $this->parserCurrentIndex++ ){
+ $cc = $this->CharCode($this->parserCurrentIndex);
+ if ((($cc >= 97) && ($cc <= 122)) || ($cc < 34)) {
+ // a-z or whitespace
+ continue;
+ }
+
+ switch ($cc) {
+
+ // (
+ case 40:
+ $parenLevel++;
+ $lastParen = $this->parserCurrentIndex;
+ break;
+
+ // )
+ case 41:
+ $parenLevel--;
+ if( $parenLevel < 0 ){
+ return $this->fail("missing opening `(`");
+ }
+ break;
+
+ // ;
+ case 59:
+ //if (!$parenLevel) { $this->emitChunk(); }
+ break;
+
+ // {
+ case 123:
+ $level++;
+ $lastOpening = $this->parserCurrentIndex;
+ break;
+
+ // }
+ case 125:
+ $level--;
+ if( $level < 0 ){
+ return $this->fail("missing opening `{`");
+
+ }
+ //if (!$level && !$parenLevel) { $this->emitChunk(); }
+ break;
+ // \
+ case 92:
+ if ($this->parserCurrentIndex < $this->input_len - 1) { $this->parserCurrentIndex++; break; }
+ return $this->fail("unescaped `\\`");
+
+ // ", ' and `
+ case 34:
+ case 39:
+ case 96:
+ $matched = 0;
+ $currentChunkStartIndex = $this->parserCurrentIndex;
+ for ($this->parserCurrentIndex = $this->parserCurrentIndex + 1; $this->parserCurrentIndex < $this->input_len; $this->parserCurrentIndex++) {
+ $cc2 = $this->CharCode($this->parserCurrentIndex);
+ if ($cc2 > 96) { continue; }
+ if ($cc2 == $cc) { $matched = 1; break; }
+ if ($cc2 == 92) { // \
+ if ($this->parserCurrentIndex == $this->input_len - 1) {
+ return $this->fail("unescaped `\\`");
+ }
+ $this->parserCurrentIndex++;
+ }
+ }
+ if ($matched) { break; }
+ return $this->fail("unmatched `" . chr($cc) . "`", $currentChunkStartIndex);
+
+ // /, check for comment
+ case 47:
+ if ($parenLevel || ($this->parserCurrentIndex == $this->input_len - 1)) { break; }
+ $cc2 = $this->CharCode($this->parserCurrentIndex+1);
+ if ($cc2 == 47) {
+ // //, find lnfeed
+ for ($this->parserCurrentIndex = $this->parserCurrentIndex + 2; $this->parserCurrentIndex < $this->input_len; $this->parserCurrentIndex++) {
+ $cc2 = $this->CharCode($this->parserCurrentIndex);
+ if (($cc2 <= 13) && (($cc2 == 10) || ($cc2 == 13))) { break; }
+ }
+ } else if ($cc2 == 42) {
+ // /*, find */
+ $lastMultiComment = $currentChunkStartIndex = $this->parserCurrentIndex;
+ for ($this->parserCurrentIndex = $this->parserCurrentIndex + 2; $this->parserCurrentIndex < $this->input_len - 1; $this->parserCurrentIndex++) {
+ $cc2 = $this->CharCode($this->parserCurrentIndex);
+ if ($cc2 == 125) { $lastMultiCommentEndBrace = $this->parserCurrentIndex; }
+ if ($cc2 != 42) { continue; }
+ if ($this->CharCode($this->parserCurrentIndex+1) == 47) { break; }
+ }
+ if ($this->parserCurrentIndex == $this->input_len - 1) {
+ return $this->fail("missing closing `*/`", $currentChunkStartIndex);
+ }
+ }
+ break;
+
+ // *, check for unmatched */
+ case 42:
+ if (($this->parserCurrentIndex < $this->input_len - 1) && ($this->CharCode($this->parserCurrentIndex+1) == 47)) {
+ return $this->fail("unmatched `/*`");
+ }
+ break;
+ }
+ }
+
+ if( $level !== 0 ){
+ if( ($lastMultiComment > $lastOpening) && ($lastMultiCommentEndBrace > $lastMultiComment) ){
+ return $this->fail("missing closing `}` or `*/`", $lastOpening);
+ } else {
+ return $this->fail("missing closing `}`", $lastOpening);
+ }
+ } else if ( $parenLevel !== 0 ){
+ return $this->fail("missing closing `)`", $lastParen);
+ }
+
+
+ //chunk didn't fail
+
+
+ //$this->emitChunk(true);
+ }
+
+ public function CharCode($pos){
+ return ord($this->input[$pos]);
+ }
+
+
+ public function fail( $msg, $index = null ){
+
+ if( !$index ){
+ $this->index = $this->parserCurrentIndex;
+ }else{
+ $this->index = $index;
+ }
+ $this->message = 'ParseError: '.$msg;
+ }
+
+
+ /*
+ function emitChunk( $force = false ){
+ $len = $this->parserCurrentIndex - $this->emitFrom;
+ if ((($len < 512) && !$force) || !$len) {
+ return;
+ }
+ $chunks[] = substr($this->input, $this->emitFrom, $this->parserCurrentIndex + 1 - $this->emitFrom );
+ $this->emitFrom = $this->parserCurrentIndex + 1;
+ }
+ */
+
+}
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Exception/Compiler.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Exception/Compiler.php
new file mode 100644
index 000000000..713e030d0
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Exception/Compiler.php
@@ -0,0 +1,11 @@
+previous = $previous;
+ parent::__construct($message, $code);
+ } else {
+ parent::__construct($message, $code, $previous);
+ }
+
+ $this->currentFile = $currentFile;
+ $this->index = $index;
+
+ $this->genMessage();
+ }
+
+
+ protected function getInput(){
+
+ if( !$this->input && $this->currentFile && $this->currentFile['filename'] && file_exists($this->currentFile['filename']) ){
+ $this->input = file_get_contents( $this->currentFile['filename'] );
+ }
+ }
+
+
+
+ /**
+ * Converts the exception to string
+ *
+ * @return string
+ */
+ public function genMessage(){
+
+ if( $this->currentFile && $this->currentFile['filename'] ){
+ $this->message .= ' in '.basename($this->currentFile['filename']);
+ }
+
+ if( $this->index !== null ){
+ $this->getInput();
+ if( $this->input ){
+ $line = self::getLineNumber();
+ $this->message .= ' on line '.$line.', column '.self::getColumn();
+
+ $lines = explode("\n",$this->input);
+
+ $count = count($lines);
+ $start_line = max(0, $line-3);
+ $last_line = min($count, $start_line+6);
+ $num_len = strlen($last_line);
+ for( $i = $start_line; $i < $last_line; $i++ ){
+ $this->message .= "\n".str_pad($i+1,$num_len,'0',STR_PAD_LEFT).'| '.$lines[$i];
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Returns the line number the error was encountered
+ *
+ * @return integer
+ */
+ public function getLineNumber(){
+ if( $this->index ){
+ // https://bugs.php.net/bug.php?id=49790
+ if (ini_get("mbstring.func_overload")) {
+ return substr_count(substr($this->input, 0, $this->index), "\n") + 1;
+ } else {
+ return substr_count($this->input, "\n", 0, $this->index) + 1;
+ }
+ }
+ return 1;
+ }
+
+
+ /**
+ * Returns the column the error was encountered
+ *
+ * @return integer
+ */
+ public function getColumn(){
+
+ $part = substr($this->input, 0, $this->index);
+ $pos = strrpos($part,"\n");
+ return $this->index - $pos;
+ }
+
+}
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Functions.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Functions.php
new file mode 100644
index 000000000..a61e5ac3c
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Functions.php
@@ -0,0 +1,1188 @@
+env = $env;
+ $this->currentFileInfo = $currentFileInfo;
+ }
+
+ /**
+ * @param string $op
+ */
+ public static function operate( $op, $a, $b ){
+ switch ($op) {
+ case '+': return $a + $b;
+ case '-': return $a - $b;
+ case '*': return $a * $b;
+ case '/': return $a / $b;
+ }
+ }
+
+ public static function clamp($val, $max = 1){
+ return min( max($val, 0), $max);
+ }
+
+ public static function fround( $value ){
+
+ if( $value === 0 ){
+ return $value;
+ }
+
+ if( Less_Parser::$options['numPrecision'] ){
+ $p = pow(10, Less_Parser::$options['numPrecision']);
+ return round( $value * $p) / $p;
+ }
+ return $value;
+ }
+
+ public static function number($n){
+
+ if ($n instanceof Less_Tree_Dimension) {
+ return floatval( $n->unit->is('%') ? $n->value / 100 : $n->value);
+ } else if (is_numeric($n)) {
+ return $n;
+ } else {
+ throw new Less_Exception_Compiler("color functions take numbers as parameters");
+ }
+ }
+
+ public static function scaled($n, $size = 255 ){
+ if( $n instanceof Less_Tree_Dimension && $n->unit->is('%') ){
+ return (float)$n->value * $size / 100;
+ } else {
+ return Less_Functions::number($n);
+ }
+ }
+
+ public function rgb ($r = null, $g = null, $b = null){
+ if (is_null($r) || is_null($g) || is_null($b)) {
+ throw new Less_Exception_Compiler("rgb expects three parameters");
+ }
+ return $this->rgba($r, $g, $b, 1.0);
+ }
+
+ public function rgba($r = null, $g = null, $b = null, $a = null){
+ $rgb = array($r, $g, $b);
+ $rgb = array_map(array('Less_Functions','scaled'),$rgb);
+
+ $a = self::number($a);
+ return new Less_Tree_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::clamp(self::number($s));
+ $l = self::clamp(self::number($l));
+ $a = self::clamp(self::number($a));
+
+ $m2 = $l <= 0.5 ? $l * ($s + 1) : $l + $s - $l * $s;
+
+ $m1 = $l * 2 - $m2;
+
+ return $this->rgba( self::hsla_hue($h + 1/3, $m1, $m2) * 255,
+ self::hsla_hue($h, $m1, $m2) * 255,
+ self::hsla_hue($h - 1/3, $m1, $m2) * 255,
+ $a);
+ }
+
+ /**
+ * @param double $h
+ */
+ public function hsla_hue($h, $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;
+ }
+
+ public function hsv($h, $s, $v) {
+ return $this->hsva($h, $s, $v, 1.0);
+ }
+
+ /**
+ * @param double $a
+ */
+ public function hsva($h, $s, $v, $a) {
+ $h = ((Less_Functions::number($h) % 360) / 360 ) * 360;
+ $s = Less_Functions::number($s);
+ $v = Less_Functions::number($v);
+ $a = Less_Functions::number($a);
+
+ $i = floor(($h / 60) % 6);
+ $f = ($h / 60) - $i;
+
+ $vs = array( $v,
+ $v * (1 - $s),
+ $v * (1 - $f * $s),
+ $v * (1 - (1 - $f) * $s));
+
+ $perm = array(array(0, 3, 1),
+ array(2, 0, 1),
+ array(1, 0, 3),
+ array(1, 2, 0),
+ array(3, 1, 0),
+ array(0, 1, 2));
+
+ return $this->rgba($vs[$perm[$i][0]] * 255,
+ $vs[$perm[$i][1]] * 255,
+ $vs[$perm[$i][2]] * 255,
+ $a);
+ }
+
+ public function hue($color = null){
+ if (!$color instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to hue must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ $c = $color->toHSL();
+ return new Less_Tree_Dimension(Less_Parser::round($c['h']));
+ }
+
+ public function saturation($color = null){
+ if (!$color instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to saturation must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ $c = $color->toHSL();
+ return new Less_Tree_Dimension(Less_Parser::round($c['s'] * 100), '%');
+ }
+
+ public function lightness($color = null){
+ if (!$color instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to lightness must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ $c = $color->toHSL();
+ return new Less_Tree_Dimension(Less_Parser::round($c['l'] * 100), '%');
+ }
+
+ public function hsvhue( $color = null ){
+ if (!$color instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to hsvhue must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ $hsv = $color->toHSV();
+ return new Less_Tree_Dimension( Less_Parser::round($hsv['h']) );
+ }
+
+
+ public function hsvsaturation( $color = null ){
+ if (!$color instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to hsvsaturation must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ $hsv = $color->toHSV();
+ return new Less_Tree_Dimension( Less_Parser::round($hsv['s'] * 100), '%' );
+ }
+
+ public function hsvvalue( $color = null ){
+ if (!$color instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to hsvvalue must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ $hsv = $color->toHSV();
+ return new Less_Tree_Dimension( Less_Parser::round($hsv['v'] * 100), '%' );
+ }
+
+ public function red($color = null) {
+ if (!$color instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to red must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ return new Less_Tree_Dimension( $color->rgb[0] );
+ }
+
+ public function green($color = null) {
+ if (!$color instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to green must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ return new Less_Tree_Dimension( $color->rgb[1] );
+ }
+
+ public function blue($color = null) {
+ if (!$color instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to blue must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ return new Less_Tree_Dimension( $color->rgb[2] );
+ }
+
+ public function alpha($color = null){
+ if (!$color instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to alpha must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ $c = $color->toHSL();
+ return new Less_Tree_Dimension($c['a']);
+ }
+
+ public function luma ($color = null) {
+ if (!$color instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to luma must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ return new Less_Tree_Dimension(Less_Parser::round( $color->luma() * $color->alpha * 100), '%');
+ }
+
+ public function luminance( $color = null ){
+ if (!$color instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to luminance must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ $luminance =
+ (0.2126 * $color->rgb[0] / 255)
+ + (0.7152 * $color->rgb[1] / 255)
+ + (0.0722 * $color->rgb[2] / 255);
+
+ return new Less_Tree_Dimension(Less_Parser::round( $luminance * $color->alpha * 100), '%');
+ }
+
+ public function saturate($color = null, $amount = null){
+ // filter: saturate(3.2);
+ // should be kept as is, so check for color
+ if ($color instanceof Less_Tree_Dimension) {
+ return null;
+ }
+
+ if (!$color instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to saturate must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+ if (!$amount instanceof Less_Tree_Dimension) {
+ throw new Less_Exception_Compiler('The second argument to saturate must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ $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']);
+ }
+
+ /**
+ * @param Less_Tree_Dimension $amount
+ */
+ public function desaturate($color = null, $amount = null){
+ if (!$color instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to desaturate must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+ if (!$amount instanceof Less_Tree_Dimension) {
+ throw new Less_Exception_Compiler('The second argument to desaturate must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ $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 = null, $amount=null){
+ if (!$color instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to lighten must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+ if (!$amount instanceof Less_Tree_Dimension) {
+ throw new Less_Exception_Compiler('The second argument to lighten must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ $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 = null, $amount = null){
+ if (!$color instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to darken must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+ if (!$amount instanceof Less_Tree_Dimension) {
+ throw new Less_Exception_Compiler('The second argument to darken must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ $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 = null, $amount = null){
+ if (!$color instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to fadein must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+ if (!$amount instanceof Less_Tree_Dimension) {
+ throw new Less_Exception_Compiler('The second argument to fadein must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ $hsl = $color->toHSL();
+ $hsl['a'] += $amount->value / 100;
+ $hsl['a'] = self::clamp($hsl['a']);
+ return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
+ }
+
+ public function fadeout($color = null, $amount = null){
+ if (!$color instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to fadeout must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+ if (!$amount instanceof Less_Tree_Dimension) {
+ throw new Less_Exception_Compiler('The second argument to fadeout must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ $hsl = $color->toHSL();
+ $hsl['a'] -= $amount->value / 100;
+ $hsl['a'] = self::clamp($hsl['a']);
+ return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
+ }
+
+ public function fade($color = null, $amount = null){
+ if (!$color instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to fade must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+ if (!$amount instanceof Less_Tree_Dimension) {
+ throw new Less_Exception_Compiler('The second argument to fade must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ $hsl = $color->toHSL();
+
+ $hsl['a'] = $amount->value / 100;
+ $hsl['a'] = self::clamp($hsl['a']);
+ return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
+ }
+
+
+
+ public function spin($color = null, $amount = null){
+ if (!$color instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to spin must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+ if (!$amount instanceof Less_Tree_Dimension) {
+ throw new Less_Exception_Compiler('The second argument to spin must be a number' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ $hsl = $color->toHSL();
+ $hue = fmod($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
+ //
+
+ /**
+ * @param Less_Tree_Color $color1
+ */
+ public function mix($color1 = null, $color2 = null, $weight = null){
+ if (!$color1 instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to mix must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+ if (!$color2 instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The second argument to mix must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+ if (!$weight) {
+ $weight = new Less_Tree_Dimension('50', '%');
+ }
+ if (!$weight instanceof Less_Tree_Dimension) {
+ throw new Less_Exception_Compiler('The third argument to contrast must be a percentage' . ($weight instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ $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_Tree_Color($rgb, $alpha);
+ }
+
+ public function greyscale($color){
+ return $this->desaturate($color, new Less_Tree_Dimension(100,'%'));
+ }
+
+
+ public function contrast( $color, $dark = null, $light = null, $threshold = null){
+ // filter: contrast(3.2);
+ // should be kept as is, so check for color
+ if (!$color instanceof Less_Tree_Color) {
+ return null;
+ }
+ if( !$light ){
+ $light = $this->rgba(255, 255, 255, 1.0);
+ }
+ if( !$dark ){
+ $dark = $this->rgba(0, 0, 0, 1.0);
+ }
+
+ if (!$dark instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The second argument to contrast must be a color' . ($dark instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+ if (!$light instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The third argument to contrast must be a color' . ($light instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ //Figure out which is actually light and dark!
+ if( $dark->luma() > $light->luma() ){
+ $t = $light;
+ $light = $dark;
+ $dark = $t;
+ }
+ if( !$threshold ){
+ $threshold = 0.43;
+ } else {
+ $threshold = Less_Functions::number($threshold);
+ }
+
+ if( $color->luma() < $threshold ){
+ return $light;
+ } else {
+ return $dark;
+ }
+ }
+
+ public function e ($str){
+ if( is_string($str) ){
+ return new Less_Tree_Anonymous($str);
+ }
+ return new Less_Tree_Anonymous($str instanceof Less_Tree_JavaScript ? $str->expression : $str->value);
+ }
+
+ public function escape ($str){
+
+ $revert = array('%21'=>'!', '%2A'=>'*', '%27'=>"'",'%3F'=>'?','%26'=>'&','%2C'=>',','%2F'=>'/','%40'=>'@','%2B'=>'+','%24'=>'$');
+
+ return new Less_Tree_Anonymous(strtr(rawurlencode($str->value), $revert));
+ }
+
+
+ /**
+ * todo: This function will need some additional work to make it work the same as less.js
+ *
+ */
+ public function replace( $string, $pattern, $replacement, $flags = null ){
+ $result = $string->value;
+
+ $expr = '/'.str_replace('/','\\/',$pattern->value).'/';
+ if( $flags && $flags->value){
+ $expr .= self::replace_flags($flags->value);
+ }
+
+ $result = preg_replace($expr,$replacement->value,$result);
+
+
+ if( property_exists($string,'quote') ){
+ return new Less_Tree_Quoted( $string->quote, $result, $string->escaped);
+ }
+ return new Less_Tree_Quoted( '', $result );
+ }
+
+ public static function replace_flags($flags){
+ $flags = str_split($flags,1);
+ $new_flags = '';
+
+ foreach($flags as $flag){
+ switch($flag){
+ case 'e':
+ case 'g':
+ break;
+
+ default:
+ $new_flags .= $flag;
+ break;
+ }
+ }
+
+ return $new_flags;
+ }
+
+ public function _percent(){
+ $string = func_get_arg(0);
+
+ $args = func_get_args();
+ array_shift($args);
+ $result = $string->value;
+
+ foreach($args as $arg){
+ if( preg_match('/%[sda]/i',$result, $token) ){
+ $token = $token[0];
+ $value = stristr($token, 's') ? $arg->value : $arg->toCSS();
+ $value = preg_match('/[A-Z]$/', $token) ? urlencode($value) : $value;
+ $result = preg_replace('/%[sda]/i',$value, $result, 1);
+ }
+ }
+ $result = str_replace('%%', '%', $result);
+
+ return new Less_Tree_Quoted( $string->quote , $result, $string->escaped);
+ }
+
+ public function unit( $val, $unit = null) {
+ if( !($val instanceof Less_Tree_Dimension) ){
+ throw new Less_Exception_Compiler('The first argument to unit must be a number' . ($val instanceof Less_Tree_Operation ? '. Have you forgotten parenthesis?' : '.') );
+ }
+
+ if( $unit ){
+ if( $unit instanceof Less_Tree_Keyword ){
+ $unit = $unit->value;
+ } else {
+ $unit = $unit->toCSS();
+ }
+ } else {
+ $unit = "";
+ }
+ return new Less_Tree_Dimension($val->value, $unit );
+ }
+
+ public function convert($val, $unit){
+ return $val->convertTo($unit->value);
+ }
+
+ public function round($n, $f = false) {
+
+ $fraction = 0;
+ if( $f !== false ){
+ $fraction = $f->value;
+ }
+
+ return $this->_math('Less_Parser::round',null, $n, $fraction);
+ }
+
+ public function pi(){
+ return new Less_Tree_Dimension(M_PI);
+ }
+
+ public function mod($a, $b) {
+ return new Less_Tree_Dimension( $a->value % $b->value, $a->unit);
+ }
+
+
+
+ public function pow($x, $y) {
+ if( is_numeric($x) && is_numeric($y) ){
+ $x = new Less_Tree_Dimension($x);
+ $y = new Less_Tree_Dimension($y);
+ }elseif( !($x instanceof Less_Tree_Dimension) || !($y instanceof Less_Tree_Dimension) ){
+ throw new Less_Exception_Compiler('Arguments must be numbers');
+ }
+
+ return new Less_Tree_Dimension( pow($x->value, $y->value), $x->unit );
+ }
+
+ // var mathFunctions = [{name:"ce ...
+ public function ceil( $n ){ return $this->_math('ceil', null, $n); }
+ public function floor( $n ){ return $this->_math('floor', null, $n); }
+ public function sqrt( $n ){ return $this->_math('sqrt', null, $n); }
+ public function abs( $n ){ return $this->_math('abs', null, $n); }
+
+ public function tan( $n ){ return $this->_math('tan', '', $n); }
+ public function sin( $n ){ return $this->_math('sin', '', $n); }
+ public function cos( $n ){ return $this->_math('cos', '', $n); }
+
+ public function atan( $n ){ return $this->_math('atan', 'rad', $n); }
+ public function asin( $n ){ return $this->_math('asin', 'rad', $n); }
+ public function acos( $n ){ return $this->_math('acos', 'rad', $n); }
+
+ private function _math() {
+ $args = func_get_args();
+ $fn = array_shift($args);
+ $unit = array_shift($args);
+
+ if ($args[0] instanceof Less_Tree_Dimension) {
+
+ if( $unit === null ){
+ $unit = $args[0]->unit;
+ }else{
+ $args[0] = $args[0]->unify();
+ }
+ $args[0] = (float)$args[0]->value;
+ return new Less_Tree_Dimension( call_user_func_array($fn, $args), $unit);
+ } else if (is_numeric($args[0])) {
+ return call_user_func_array($fn,$args);
+ } else {
+ throw new Less_Exception_Compiler("math functions take numbers as parameters");
+ }
+ }
+
+ /**
+ * @param boolean $isMin
+ */
+ private function _minmax( $isMin, $args ){
+
+ $arg_count = count($args);
+
+ if( $arg_count < 1 ){
+ throw new Less_Exception_Compiler( 'one or more arguments required');
+ }
+
+ $j = null;
+ $unitClone = null;
+ $unitStatic = null;
+
+
+ $order = array(); // elems only contains original argument values.
+ $values = array(); // key is the unit.toString() for unified tree.Dimension values,
+ // value is the index into the order array.
+
+
+ for( $i = 0; $i < $arg_count; $i++ ){
+ $current = $args[$i];
+ if( !($current instanceof Less_Tree_Dimension) ){
+ if( is_array($args[$i]->value) ){
+ $args[] = $args[$i]->value;
+ }
+ continue;
+ }
+
+ if( $current->unit->toString() === '' && !$unitClone ){
+ $temp = new Less_Tree_Dimension($current->value, $unitClone);
+ $currentUnified = $temp->unify();
+ }else{
+ $currentUnified = $current->unify();
+ }
+
+ if( $currentUnified->unit->toString() === "" && !$unitStatic ){
+ $unit = $unitStatic;
+ }else{
+ $unit = $currentUnified->unit->toString();
+ }
+
+ if( $unit !== '' && !$unitStatic || $unit !== '' && $order[0]->unify()->unit->toString() === "" ){
+ $unitStatic = $unit;
+ }
+
+ if( $unit != '' && !$unitClone ){
+ $unitClone = $current->unit->toString();
+ }
+
+ if( isset($values['']) && $unit !== '' && $unit === $unitStatic ){
+ $j = $values[''];
+ }elseif( isset($values[$unit]) ){
+ $j = $values[$unit];
+ }else{
+
+ if( $unitStatic && $unit !== $unitStatic ){
+ throw new Less_Exception_Compiler( 'incompatible types');
+ }
+ $values[$unit] = count($order);
+ $order[] = $current;
+ continue;
+ }
+
+
+ if( $order[$j]->unit->toString() === "" && $unitClone ){
+ $temp = new Less_Tree_Dimension( $order[$j]->value, $unitClone);
+ $referenceUnified = $temp->unify();
+ }else{
+ $referenceUnified = $order[$j]->unify();
+ }
+ if( ($isMin && $currentUnified->value < $referenceUnified->value) || (!$isMin && $currentUnified->value > $referenceUnified->value) ){
+ $order[$j] = $current;
+ }
+ }
+
+ if( count($order) == 1 ){
+ return $order[0];
+ }
+ $args = array();
+ foreach($order as $a){
+ $args[] = $a->toCSS($this->env);
+ }
+ return new Less_Tree_Anonymous( ($isMin?'min(':'max(') . implode(Less_Environment::$_outputMap[','],$args).')');
+ }
+
+ public function min(){
+ $args = func_get_args();
+ return $this->_minmax( true, $args );
+ }
+
+ public function max(){
+ $args = func_get_args();
+ return $this->_minmax( false, $args );
+ }
+
+ public function getunit($n){
+ return new Less_Tree_Anonymous($n->unit);
+ }
+
+ public function argb($color) {
+ if (!$color instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to argb must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ return new Less_Tree_Anonymous($color->toARGB());
+ }
+
+ public function percentage($n) {
+ return new Less_Tree_Dimension($n->value * 100, '%');
+ }
+
+ public function color($n) {
+
+ if( $n instanceof Less_Tree_Quoted ){
+ $colorCandidate = $n->value;
+ $returnColor = Less_Tree_Color::fromKeyword($colorCandidate);
+ if( $returnColor ){
+ return $returnColor;
+ }
+ if( preg_match('/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/',$colorCandidate) ){
+ return new Less_Tree_Color(substr($colorCandidate, 1));
+ }
+ throw new Less_Exception_Compiler("argument must be a color keyword or 3/6 digit hex e.g. #FFF");
+ } else {
+ throw new Less_Exception_Compiler("argument must be a string");
+ }
+ }
+
+
+ public function iscolor($n) {
+ return $this->_isa($n, 'Less_Tree_Color');
+ }
+
+ public function isnumber($n) {
+ return $this->_isa($n, 'Less_Tree_Dimension');
+ }
+
+ public function isstring($n) {
+ return $this->_isa($n, 'Less_Tree_Quoted');
+ }
+
+ public function iskeyword($n) {
+ return $this->_isa($n, 'Less_Tree_Keyword');
+ }
+
+ public function isurl($n) {
+ return $this->_isa($n, 'Less_Tree_Url');
+ }
+
+ public function ispixel($n) {
+ return $this->isunit($n, 'px');
+ }
+
+ public function ispercentage($n) {
+ return $this->isunit($n, '%');
+ }
+
+ public function isem($n) {
+ return $this->isunit($n, 'em');
+ }
+
+ /**
+ * @param string $unit
+ */
+ public function isunit( $n, $unit ){
+
+ if( is_object($unit) && property_exists($unit,'value') ){
+ $unit = $unit->value;
+ }
+
+ return ($n instanceof Less_Tree_Dimension) && $n->unit->is($unit) ? new Less_Tree_Keyword('true') : new Less_Tree_Keyword('false');
+ }
+
+ /**
+ * @param string $type
+ */
+ private function _isa($n, $type) {
+ return is_a($n, $type) ? new Less_Tree_Keyword('true') : new Less_Tree_Keyword('false');
+ }
+
+ public function tint($color, $amount = null) {
+ return $this->mix( $this->rgb(255,255,255), $color, $amount);
+ }
+
+ public function shade($color, $amount = null) {
+ return $this->mix($this->rgb(0, 0, 0), $color, $amount);
+ }
+
+ public function extract($values, $index ){
+ $index = (int)$index->value - 1; // (1-based index)
+ // handle non-array values as an array of length 1
+ // return 'undefined' if index is invalid
+ if( property_exists($values,'value') && is_array($values->value) ){
+ if( isset($values->value[$index]) ){
+ return $values->value[$index];
+ }
+ return null;
+
+ }elseif( (int)$index === 0 ){
+ return $values;
+ }
+
+ return null;
+ }
+
+ public function length($values){
+ $n = (property_exists($values,'value') && is_array($values->value)) ? count($values->value) : 1;
+ return new Less_Tree_Dimension($n);
+ }
+
+ public function datauri($mimetypeNode, $filePathNode = null ) {
+
+ $filePath = ( $filePathNode ? $filePathNode->value : null );
+ $mimetype = $mimetypeNode->value;
+
+ $args = 2;
+ if( !$filePath ){
+ $filePath = $mimetype;
+ $args = 1;
+ }
+
+ $filePath = str_replace('\\','/',$filePath);
+ if( Less_Environment::isPathRelative($filePath) ){
+
+ if( Less_Parser::$options['relativeUrls'] ){
+ $temp = $this->currentFileInfo['currentDirectory'];
+ } else {
+ $temp = $this->currentFileInfo['entryPath'];
+ }
+
+ if( !empty($temp) ){
+ $filePath = Less_Environment::normalizePath(rtrim($temp,'/').'/'.$filePath);
+ }
+
+ }
+
+
+ // detect the mimetype if not given
+ if( $args < 2 ){
+
+ /* incomplete
+ $mime = require('mime');
+ mimetype = mime.lookup(path);
+
+ // use base 64 unless it's an ASCII or UTF-8 format
+ var charset = mime.charsets.lookup(mimetype);
+ useBase64 = ['US-ASCII', 'UTF-8'].indexOf(charset) < 0;
+ if (useBase64) mimetype += ';base64';
+ */
+
+ $mimetype = Less_Mime::lookup($filePath);
+
+ $charset = Less_Mime::charsets_lookup($mimetype);
+ $useBase64 = !in_array($charset,array('US-ASCII', 'UTF-8'));
+ if( $useBase64 ){ $mimetype .= ';base64'; }
+
+ }else{
+ $useBase64 = preg_match('/;base64$/',$mimetype);
+ }
+
+
+ if( file_exists($filePath) ){
+ $buf = @file_get_contents($filePath);
+ }else{
+ $buf = false;
+ }
+
+
+ // IE8 cannot handle a data-uri larger than 32KB. If this is exceeded
+ // and the --ieCompat flag is enabled, return a normal url() instead.
+ $DATA_URI_MAX_KB = 32;
+ $fileSizeInKB = round( strlen($buf) / 1024 );
+ if( $fileSizeInKB >= $DATA_URI_MAX_KB ){
+ $url = new Less_Tree_Url( ($filePathNode ? $filePathNode : $mimetypeNode), $this->currentFileInfo);
+ return $url->compile($this);
+ }
+
+ if( $buf ){
+ $buf = $useBase64 ? base64_encode($buf) : rawurlencode($buf);
+ $filePath = '"data:' . $mimetype . ',' . $buf . '"';
+ }
+
+ return new Less_Tree_Url( new Less_Tree_Anonymous($filePath) );
+ }
+
+ //svg-gradient
+ public function svggradient( $direction ){
+
+ $throw_message = 'svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]';
+ $arguments = func_get_args();
+
+ if( count($arguments) < 3 ){
+ throw new Less_Exception_Compiler( $throw_message );
+ }
+
+ $stops = array_slice($arguments,1);
+ $gradientType = 'linear';
+ $rectangleDimension = 'x="0" y="0" width="1" height="1"';
+ $useBase64 = true;
+ $directionValue = $direction->toCSS();
+
+
+ switch( $directionValue ){
+ case "to bottom":
+ $gradientDirectionSvg = 'x1="0%" y1="0%" x2="0%" y2="100%"';
+ break;
+ case "to right":
+ $gradientDirectionSvg = 'x1="0%" y1="0%" x2="100%" y2="0%"';
+ break;
+ case "to bottom right":
+ $gradientDirectionSvg = 'x1="0%" y1="0%" x2="100%" y2="100%"';
+ break;
+ case "to top right":
+ $gradientDirectionSvg = 'x1="0%" y1="100%" x2="100%" y2="0%"';
+ break;
+ case "ellipse":
+ case "ellipse at center":
+ $gradientType = "radial";
+ $gradientDirectionSvg = 'cx="50%" cy="50%" r="75%"';
+ $rectangleDimension = 'x="-50" y="-50" width="101" height="101"';
+ break;
+ default:
+ throw new Less_Exception_Compiler( "svg-gradient direction must be 'to bottom', 'to right', 'to bottom right', 'to top right' or 'ellipse at center'" );
+ }
+
+ $returner = '' .
+ '' .
+ '<' . $gradientType . 'Gradient id="gradient" gradientUnits="userSpaceOnUse" ' . $gradientDirectionSvg . '>';
+
+ for( $i = 0; $i < count($stops); $i++ ){
+ if( is_object($stops[$i]) && property_exists($stops[$i],'value') ){
+ $color = $stops[$i]->value[0];
+ $position = $stops[$i]->value[1];
+ }else{
+ $color = $stops[$i];
+ $position = null;
+ }
+
+ if( !($color instanceof Less_Tree_Color) || (!(($i === 0 || $i+1 === count($stops)) && $position === null) && !($position instanceof Less_Tree_Dimension)) ){
+ throw new Less_Exception_Compiler( $throw_message );
+ }
+ if( $position ){
+ $positionValue = $position->toCSS();
+ }elseif( $i === 0 ){
+ $positionValue = '0%';
+ }else{
+ $positionValue = '100%';
+ }
+ $alpha = $color->alpha;
+ $returner .= ' ';
+ }
+
+ $returner .= '' . $gradientType . 'Gradient> ';
+
+
+ if( $useBase64 ){
+ $returner = "'data:image/svg+xml;base64,".base64_encode($returner)."'";
+ }else{
+ $returner = "'data:image/svg+xml,".$returner."'";
+ }
+
+ return new Less_Tree_URL( new Less_Tree_Anonymous( $returner ) );
+ }
+
+
+ /**
+ * Php version of javascript's `encodeURIComponent` function
+ *
+ * @param string $string The string to encode
+ * @return string The encoded string
+ */
+ public static function encodeURIComponent($string){
+ $revert = array('%21' => '!', '%2A' => '*', '%27' => "'", '%28' => '(', '%29' => ')');
+ return strtr(rawurlencode($string), $revert);
+ }
+
+
+ // Color Blending
+ // ref: http://www.w3.org/TR/compositing-1
+
+ public function colorBlend( $mode, $color1, $color2 ){
+ $ab = $color1->alpha; // backdrop
+ $as = $color2->alpha; // source
+ $r = array(); // result
+
+ $ar = $as + $ab * (1 - $as);
+ for( $i = 0; $i < 3; $i++ ){
+ $cb = $color1->rgb[$i] / 255;
+ $cs = $color2->rgb[$i] / 255;
+ $cr = call_user_func( $mode, $cb, $cs );
+ if( $ar ){
+ $cr = ($as * $cs + $ab * ($cb - $as * ($cb + $cs - $cr))) / $ar;
+ }
+ $r[$i] = $cr * 255;
+ }
+
+ return new Less_Tree_Color($r, $ar);
+ }
+
+ public function multiply($color1 = null, $color2 = null ){
+ if (!$color1 instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to multiply must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+ if (!$color2 instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The second argument to multiply must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ return $this->colorBlend( array($this,'colorBlendMultiply'), $color1, $color2 );
+ }
+
+ private function colorBlendMultiply($cb, $cs){
+ return $cb * $cs;
+ }
+
+ public function screen($color1 = null, $color2 = null ){
+ if (!$color1 instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to screen must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+ if (!$color2 instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The second argument to screen must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ return $this->colorBlend( array($this,'colorBlendScreen'), $color1, $color2 );
+ }
+
+ private function colorBlendScreen( $cb, $cs){
+ return $cb + $cs - $cb * $cs;
+ }
+
+ public function overlay($color1 = null, $color2 = null){
+ if (!$color1 instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to overlay must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+ if (!$color2 instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The second argument to overlay must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ return $this->colorBlend( array($this,'colorBlendOverlay'), $color1, $color2 );
+ }
+
+ private function colorBlendOverlay($cb, $cs ){
+ $cb *= 2;
+ return ($cb <= 1)
+ ? $this->colorBlendMultiply($cb, $cs)
+ : $this->colorBlendScreen($cb - 1, $cs);
+ }
+
+ public function softlight($color1 = null, $color2 = null){
+ if (!$color1 instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to softlight must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+ if (!$color2 instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The second argument to softlight must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ return $this->colorBlend( array($this,'colorBlendSoftlight'), $color1, $color2 );
+ }
+
+ private function colorBlendSoftlight($cb, $cs ){
+ $d = 1;
+ $e = $cb;
+ if( $cs > 0.5 ){
+ $e = 1;
+ $d = ($cb > 0.25) ? sqrt($cb)
+ : ((16 * $cb - 12) * $cb + 4) * $cb;
+ }
+ return $cb - (1 - 2 * $cs) * $e * ($d - $cb);
+ }
+
+ public function hardlight($color1 = null, $color2 = null){
+ if (!$color1 instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to hardlight must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+ if (!$color2 instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The second argument to hardlight must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ return $this->colorBlend( array($this,'colorBlendHardlight'), $color1, $color2 );
+ }
+
+ private function colorBlendHardlight( $cb, $cs ){
+ return $this->colorBlendOverlay($cs, $cb);
+ }
+
+ public function difference($color1 = null, $color2 = null) {
+ if (!$color1 instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to difference must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+ if (!$color2 instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The second argument to difference must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ return $this->colorBlend( array($this,'colorBlendDifference'), $color1, $color2 );
+ }
+
+ private function colorBlendDifference( $cb, $cs ){
+ return abs($cb - $cs);
+ }
+
+ public function exclusion( $color1 = null, $color2 = null ){
+ if (!$color1 instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to exclusion must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+ if (!$color2 instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The second argument to exclusion must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ return $this->colorBlend( array($this,'colorBlendExclusion'), $color1, $color2 );
+ }
+
+ private function colorBlendExclusion( $cb, $cs ){
+ return $cb + $cs - 2 * $cb * $cs;
+ }
+
+ public function average($color1 = null, $color2 = null){
+ if (!$color1 instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to average must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+ if (!$color2 instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The second argument to average must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ return $this->colorBlend( array($this,'colorBlendAverage'), $color1, $color2 );
+ }
+
+ // non-w3c functions:
+ public function colorBlendAverage($cb, $cs ){
+ return ($cb + $cs) / 2;
+ }
+
+ public function negation($color1 = null, $color2 = null ){
+ if (!$color1 instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to negation must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+ if (!$color2 instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The second argument to negation must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ return $this->colorBlend( array($this,'colorBlendNegation'), $color1, $color2 );
+ }
+
+ public function colorBlendNegation($cb, $cs){
+ return 1 - abs($cb + $cs - 1);
+ }
+
+ // ~ End of Color Blending
+
+}
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Less.php.combine b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Less.php.combine
new file mode 100644
index 000000000..d63cc7898
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Less.php.combine
@@ -0,0 +1,17 @@
+
+./Parser.php
+./Colors.php
+./Environment.php
+./Functions.php
+./Mime.php
+./Tree.php
+./Output.php
+./Visitor.php
+./VisitorReplacing.php
+./Configurable.php
+./Tree
+./Visitor
+./Exception/Parser.php
+./Exception/
+./Output
+./SourceMap
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Mime.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Mime.php
new file mode 100644
index 000000000..109ecd3f8
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Mime.php
@@ -0,0 +1,41 @@
+ 'text/html',
+ '.html'=> 'text/html',
+ '.gif' => 'image/gif',
+ '.jpg' => 'image/jpeg',
+ '.jpeg'=> 'image/jpeg',
+ '.png' => 'image/png',
+ '.ttf' => 'application/x-font-ttf',
+ '.otf' => 'application/x-font-otf',
+ '.eot' => 'application/vnd.ms-fontobject',
+ '.woff' => 'application/x-font-woff',
+ '.svg' => 'image/svg+xml',
+ );
+
+ public static function lookup( $filepath ){
+ $parts = explode('.',$filepath);
+ $ext = '.'.strtolower(array_pop($parts));
+
+ if( !isset(self::$_types[$ext]) ){
+ return null;
+ }
+ return self::$_types[$ext];
+ }
+
+ public static function charsets_lookup( $type = null ){
+ // assumes all text types are UTF-8
+ return $type && preg_match('/^text\//',$type) ? 'UTF-8' : '';
+ }
+}
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Output.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Output.php
new file mode 100644
index 000000000..f1b2b2a99
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Output.php
@@ -0,0 +1,49 @@
+strs[] = $chunk;
+ }
+
+ /**
+ * Is the output empty?
+ *
+ * @return boolean
+ */
+ public function isEmpty(){
+ return count($this->strs) === 0;
+ }
+
+
+ /**
+ * Converts the output to string
+ *
+ * @return string
+ */
+ public function toString(){
+ return implode('',$this->strs);
+ }
+
+}
\ No newline at end of file
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Output/Mapped.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Output/Mapped.php
new file mode 100644
index 000000000..884490a20
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Output/Mapped.php
@@ -0,0 +1,122 @@
+contentsMap = $contentsMap;
+ $this->generator = $generator;
+ }
+
+ /**
+ * Adds a chunk to the stack
+ * The $index for less.php may be different from less.js since less.php does not chunkify inputs
+ *
+ * @param string $chunk
+ * @param string $fileInfo
+ * @param integer $index
+ * @param mixed $mapLines
+ */
+ public function add($chunk, $fileInfo = null, $index = 0, $mapLines = null){
+
+ //ignore adding empty strings
+ if( $chunk === '' ){
+ return;
+ }
+
+
+ $sourceLines = array();
+ $sourceColumns = ' ';
+
+
+ if( $fileInfo ){
+
+ $url = $fileInfo['currentUri'];
+
+ if( isset($this->contentsMap[$url]) ){
+ $inputSource = substr($this->contentsMap[$url], 0, $index);
+ $sourceLines = explode("\n", $inputSource);
+ $sourceColumns = end($sourceLines);
+ }else{
+ throw new Exception('Filename '.$url.' not in contentsMap');
+ }
+
+ }
+
+ $lines = explode("\n", $chunk);
+ $columns = end($lines);
+
+ if($fileInfo){
+
+ if(!$mapLines){
+ $this->generator->addMapping(
+ $this->lineNumber + 1, // generated_line
+ $this->column, // generated_column
+ count($sourceLines), // original_line
+ strlen($sourceColumns), // original_column
+ $fileInfo
+ );
+ }else{
+ for($i = 0, $count = count($lines); $i < $count; $i++){
+ $this->generator->addMapping(
+ $this->lineNumber + $i + 1, // generated_line
+ $i === 0 ? $this->column : 0, // generated_column
+ count($sourceLines) + $i, // original_line
+ $i === 0 ? strlen($sourceColumns) : 0, // original_column
+ $fileInfo
+ );
+ }
+ }
+ }
+
+ if(count($lines) === 1){
+ $this->column += strlen($columns);
+ }else{
+ $this->lineNumber += count($lines) - 1;
+ $this->column = strlen($columns);
+ }
+
+ // add only chunk
+ parent::add($chunk);
+ }
+
+}
\ No newline at end of file
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Parser.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Parser.php
new file mode 100644
index 000000000..2c9d87908
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Parser.php
@@ -0,0 +1,2816 @@
+ false, // option - whether to compress
+ 'strictUnits' => false, // whether units need to evaluate correctly
+ 'strictMath' => false, // whether math has to be within parenthesis
+ 'relativeUrls' => true, // option - whether to adjust URL's to be relative
+ 'urlArgs' => '', // whether to add args into url tokens
+ 'numPrecision' => 8,
+
+ 'import_dirs' => array(),
+ 'import_callback' => null,
+ 'cache_dir' => null,
+ 'cache_method' => 'php', // false, 'serialize', 'php', 'var_export', 'callback';
+ 'cache_callback_get' => null,
+ 'cache_callback_set' => null,
+
+ 'sourceMap' => false, // whether to output a source map
+ 'sourceMapBasepath' => null,
+ 'sourceMapWriteTo' => null,
+ 'sourceMapURL' => null,
+
+ 'indentation' => ' ',
+
+ 'plugins' => array(),
+
+ );
+
+ public static $options = array();
+
+
+ private $input; // Less input string
+ private $input_len; // input string length
+ private $pos; // current index in `input`
+ private $saveStack = array(); // holds state for backtracking
+ private $furthest;
+ private $mb_internal_encoding = ''; // for remember exists value of mbstring.internal_encoding
+
+ /**
+ * @var Less_Environment
+ */
+ private $env;
+
+ protected $rules = array();
+
+ private static $imports = array();
+
+ public static $has_extends = false;
+
+ public static $next_id = 0;
+
+ /**
+ * Filename to contents of all parsed the files
+ *
+ * @var array
+ */
+ public static $contentsMap = array();
+
+
+ /**
+ * @param Less_Environment|array|null $env
+ */
+ public function __construct( $env = null ){
+
+ // Top parser on an import tree must be sure there is one "env"
+ // which will then be passed around by reference.
+ if( $env instanceof Less_Environment ){
+ $this->env = $env;
+ }else{
+ $this->SetOptions(Less_Parser::$default_options);
+ $this->Reset( $env );
+ }
+
+ // mbstring.func_overload > 1 bugfix
+ // The encoding value must be set for each source file,
+ // therefore, to conserve resources and improve the speed of this design is taken here
+ if (ini_get('mbstring.func_overload')) {
+ $this->mb_internal_encoding = ini_get('mbstring.internal_encoding');
+ @ini_set('mbstring.internal_encoding', 'ascii');
+ }
+
+ }
+
+
+ /**
+ * Reset the parser state completely
+ *
+ */
+ public function Reset( $options = null ){
+ $this->rules = array();
+ self::$imports = array();
+ self::$has_extends = false;
+ self::$imports = array();
+ self::$contentsMap = array();
+
+ $this->env = new Less_Environment($options);
+
+ //set new options
+ if( is_array($options) ){
+ $this->SetOptions(Less_Parser::$default_options);
+ $this->SetOptions($options);
+ }
+
+ $this->env->Init();
+ }
+
+ /**
+ * Set one or more compiler options
+ * options: import_dirs, cache_dir, cache_method
+ *
+ */
+ public function SetOptions( $options ){
+ foreach($options as $option => $value){
+ $this->SetOption($option,$value);
+ }
+ }
+
+ /**
+ * Set one compiler option
+ *
+ */
+ public function SetOption($option,$value){
+
+ switch($option){
+
+ case 'import_dirs':
+ $this->SetImportDirs($value);
+ return;
+
+ case 'cache_dir':
+ if( is_string($value) ){
+ Less_Cache::SetCacheDir($value);
+ Less_Cache::CheckCacheDir();
+ }
+ return;
+ }
+
+ Less_Parser::$options[$option] = $value;
+ }
+
+ /**
+ * Registers a new custom function
+ *
+ * @param string $name function name
+ * @param callable $callback callback
+ */
+ public function registerFunction($name, $callback) {
+ $this->env->functions[$name] = $callback;
+ }
+
+ /**
+ * Removed an already registered function
+ *
+ * @param string $name function name
+ */
+ public function unregisterFunction($name) {
+ if( isset($this->env->functions[$name]) )
+ unset($this->env->functions[$name]);
+ }
+
+
+ /**
+ * Get the current css buffer
+ *
+ * @return string
+ */
+ public function getCss(){
+
+ $precision = ini_get('precision');
+ @ini_set('precision',16);
+ $locale = setlocale(LC_NUMERIC, 0);
+ setlocale(LC_NUMERIC, "C");
+
+ try {
+
+ $root = new Less_Tree_Ruleset(array(), $this->rules );
+ $root->root = true;
+ $root->firstRoot = true;
+
+
+ $this->PreVisitors($root);
+
+ self::$has_extends = false;
+ $evaldRoot = $root->compile($this->env);
+
+
+
+ $this->PostVisitors($evaldRoot);
+
+ if( Less_Parser::$options['sourceMap'] ){
+ $generator = new Less_SourceMap_Generator($evaldRoot, Less_Parser::$contentsMap, Less_Parser::$options );
+ // will also save file
+ // FIXME: should happen somewhere else?
+ $css = $generator->generateCSS();
+ }else{
+ $css = $evaldRoot->toCSS();
+ }
+
+ if( Less_Parser::$options['compress'] ){
+ $css = preg_replace('/(^(\s)+)|((\s)+$)/', '', $css);
+ }
+
+ } catch (Exception $exc) {
+ // Intentional fall-through so we can reset environment
+ }
+
+ //reset php settings
+ @ini_set('precision',$precision);
+ setlocale(LC_NUMERIC, $locale);
+
+ // If you previously defined $this->mb_internal_encoding
+ // is required to return the encoding as it was before
+ if ($this->mb_internal_encoding != '') {
+ @ini_set("mbstring.internal_encoding", $this->mb_internal_encoding);
+ $this->mb_internal_encoding = '';
+ }
+
+ // Rethrow exception after we handled resetting the environment
+ if (!empty($exc)) {
+ throw $exc;
+ }
+
+ return $css;
+ }
+
+ public function findValueOf($varName)
+ {
+ foreach($this->rules as $rule){
+ if(isset($rule->variable) && ($rule->variable == true) && (str_replace("@","",$rule->name) == $varName)){
+ return $this->getVariableValue($rule);
+ }
+ }
+ return null;
+ }
+
+ /**
+ *
+ * this function gets the private rules variable and returns an array of the found variables
+ * it uses a helper method getVariableValue() that contains the logic ot fetch the value from the rule object
+ *
+ * @return array
+ */
+ public function getVariables()
+ {
+ $variables = array();
+
+ $not_variable_type = array(
+ 'Comment', // this include less comments ( // ) and css comments (/* */)
+ 'Import', // do not search variables in included files @import
+ 'Ruleset', // selectors (.someclass, #someid, …)
+ 'Operation', //
+ );
+
+ // @TODO run compilation if not runned yet
+ foreach ($this->rules as $key => $rule) {
+ if (in_array($rule->type, $not_variable_type)) {
+ continue;
+ }
+
+ // Note: it seems rule->type is always Rule when variable = true
+ if ($rule->type == 'Rule' && $rule->variable) {
+ $variables[$rule->name] = $this->getVariableValue($rule);
+ } else {
+ if ($rule->type == 'Comment') {
+ $variables[] = $this->getVariableValue($rule);
+ }
+ }
+ }
+ return $variables;
+ }
+
+ public function findVarByName($var_name)
+ {
+ foreach($this->rules as $rule){
+ if(isset($rule->variable) && ($rule->variable == true)){
+ if($rule->name == $var_name){
+ return $this->getVariableValue($rule);
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ *
+ * This method gets the value of the less variable from the rules object.
+ * Since the objects vary here we add the logic for extracting the css/less value.
+ *
+ * @param $var
+ *
+ * @return bool|string
+ */
+ private function getVariableValue($var)
+ {
+ if (!is_a($var, 'Less_Tree')) {
+ throw new Exception('var is not a Less_Tree object');
+ }
+
+ switch ($var->type) {
+ case 'Color':
+ return $this->rgb2html($var->rgb);
+ case 'Unit':
+ return $var->value. $var->unit->numerator[0];
+ case 'Variable':
+ return $this->findVarByName($var->name);
+ case 'Keyword':
+ return $var->value;
+ case 'Rule':
+ return $this->getVariableValue($var->value);
+ case 'Value':
+ $value = '';
+ foreach ($var->value as $sub_value) {
+ $value .= $this->getVariableValue($sub_value).' ';
+ }
+ return $value;
+ case 'Quoted':
+ return $var->quote.$var->value.$var->quote;
+ case 'Dimension':
+ $value = $var->value;
+ if ($var->unit && $var->unit->numerator) {
+ $value .= $var->unit->numerator[0];
+ }
+ return $value;
+ case 'Expression':
+ $value = "";
+ foreach($var->value as $item) {
+ $value .= $this->getVariableValue($item)." ";
+ }
+ return $value;
+ case 'Operation':
+ throw new Exception('getVariables() require Less to be compiled. please use $parser->getCss() before calling getVariables()');
+ case 'Comment':
+ case 'Import':
+ case 'Ruleset':
+ default:
+ throw new Exception("type missing in switch/case getVariableValue for ".$var->type);
+ }
+ return false;
+ }
+
+ private function rgb2html($r, $g=-1, $b=-1)
+ {
+ if (is_array($r) && sizeof($r) == 3)
+ list($r, $g, $b) = $r;
+
+ $r = intval($r); $g = intval($g);
+ $b = intval($b);
+
+ $r = dechex($r<0?0:($r>255?255:$r));
+ $g = dechex($g<0?0:($g>255?255:$g));
+ $b = dechex($b<0?0:($b>255?255:$b));
+
+ $color = (strlen($r) < 2?'0':'').$r;
+ $color .= (strlen($g) < 2?'0':'').$g;
+ $color .= (strlen($b) < 2?'0':'').$b;
+ return '#'.$color;
+ }
+
+ /**
+ * Run pre-compile visitors
+ *
+ */
+ private function PreVisitors($root){
+
+ if( Less_Parser::$options['plugins'] ){
+ foreach(Less_Parser::$options['plugins'] as $plugin){
+ if( !empty($plugin->isPreEvalVisitor) ){
+ $plugin->run($root);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Run post-compile visitors
+ *
+ */
+ private function PostVisitors($evaldRoot){
+
+ $visitors = array();
+ $visitors[] = new Less_Visitor_joinSelector();
+ if( self::$has_extends ){
+ $visitors[] = new Less_Visitor_processExtends();
+ }
+ $visitors[] = new Less_Visitor_toCSS();
+
+
+ if( Less_Parser::$options['plugins'] ){
+ foreach(Less_Parser::$options['plugins'] as $plugin){
+ if( property_exists($plugin,'isPreEvalVisitor') && $plugin->isPreEvalVisitor ){
+ continue;
+ }
+
+ if( property_exists($plugin,'isPreVisitor') && $plugin->isPreVisitor ){
+ array_unshift( $visitors, $plugin);
+ }else{
+ $visitors[] = $plugin;
+ }
+ }
+ }
+
+
+ for($i = 0; $i < count($visitors); $i++ ){
+ $visitors[$i]->run($evaldRoot);
+ }
+
+ }
+
+
+ /**
+ * Parse a Less string into css
+ *
+ * @param string $str The string to convert
+ * @param string $uri_root The url of the file
+ * @return Less_Tree_Ruleset|Less_Parser
+ */
+ public function parse( $str, $file_uri = null ){
+
+ if( !$file_uri ){
+ $uri_root = '';
+ $filename = 'anonymous-file-'.Less_Parser::$next_id++.'.less';
+ }else{
+ $file_uri = self::WinPath($file_uri);
+ $filename = $file_uri;
+ $uri_root = dirname($file_uri);
+ }
+
+ $previousFileInfo = $this->env->currentFileInfo;
+ $uri_root = self::WinPath($uri_root);
+ $this->SetFileInfo($filename, $uri_root);
+
+ $this->input = $str;
+ $this->_parse();
+
+ if( $previousFileInfo ){
+ $this->env->currentFileInfo = $previousFileInfo;
+ }
+
+ return $this;
+ }
+
+
+ /**
+ * Parse a Less string from a given file
+ *
+ * @throws Less_Exception_Parser
+ * @param string $filename The file to parse
+ * @param string $uri_root The url of the file
+ * @param bool $returnRoot Indicates whether the return value should be a css string a root node
+ * @return Less_Tree_Ruleset|Less_Parser
+ */
+ public function parseFile( $filename, $uri_root = '', $returnRoot = false){
+
+ if( !file_exists($filename) ){
+ $this->Error(sprintf('File `%s` not found.', $filename));
+ }
+
+
+ // fix uri_root?
+ // Instead of The mixture of file path for the first argument and directory path for the second argument has bee
+ if( !$returnRoot && !empty($uri_root) && basename($uri_root) == basename($filename) ){
+ $uri_root = dirname($uri_root);
+ }
+
+
+ $previousFileInfo = $this->env->currentFileInfo;
+
+
+ if( $filename ){
+ $filename = self::AbsPath($filename, true);
+ }
+ $uri_root = self::WinPath($uri_root);
+
+ $this->SetFileInfo($filename, $uri_root);
+
+ self::AddParsedFile($filename);
+
+ if( $returnRoot ){
+ $rules = $this->GetRules( $filename );
+ $return = new Less_Tree_Ruleset(array(), $rules );
+ }else{
+ $this->_parse( $filename );
+ $return = $this;
+ }
+
+ if( $previousFileInfo ){
+ $this->env->currentFileInfo = $previousFileInfo;
+ }
+
+ return $return;
+ }
+
+
+ /**
+ * Allows a user to set variables values
+ * @param array $vars
+ * @return Less_Parser
+ */
+ public function ModifyVars( $vars ){
+
+ $this->input = Less_Parser::serializeVars( $vars );
+ $this->_parse();
+
+ return $this;
+ }
+
+
+ /**
+ * @param string $filename
+ */
+ public function SetFileInfo( $filename, $uri_root = ''){
+
+ $filename = Less_Environment::normalizePath($filename);
+ $dirname = preg_replace('/[^\/\\\\]*$/','',$filename);
+
+ if( !empty($uri_root) ){
+ $uri_root = rtrim($uri_root,'/').'/';
+ }
+
+ $currentFileInfo = array();
+
+ //entry info
+ if( isset($this->env->currentFileInfo) ){
+ $currentFileInfo['entryPath'] = $this->env->currentFileInfo['entryPath'];
+ $currentFileInfo['entryUri'] = $this->env->currentFileInfo['entryUri'];
+ $currentFileInfo['rootpath'] = $this->env->currentFileInfo['rootpath'];
+
+ }else{
+ $currentFileInfo['entryPath'] = $dirname;
+ $currentFileInfo['entryUri'] = $uri_root;
+ $currentFileInfo['rootpath'] = $dirname;
+ }
+
+ $currentFileInfo['currentDirectory'] = $dirname;
+ $currentFileInfo['currentUri'] = $uri_root.basename($filename);
+ $currentFileInfo['filename'] = $filename;
+ $currentFileInfo['uri_root'] = $uri_root;
+
+
+ //inherit reference
+ if( isset($this->env->currentFileInfo['reference']) && $this->env->currentFileInfo['reference'] ){
+ $currentFileInfo['reference'] = true;
+ }
+
+ $this->env->currentFileInfo = $currentFileInfo;
+ }
+
+
+ /**
+ * @deprecated 1.5.1.2
+ *
+ */
+ public function SetCacheDir( $dir ){
+
+ if( !file_exists($dir) ){
+ if( mkdir($dir) ){
+ return true;
+ }
+ throw new Less_Exception_Parser('Less.php cache directory couldn\'t be created: '.$dir);
+
+ }elseif( !is_dir($dir) ){
+ throw new Less_Exception_Parser('Less.php cache directory doesn\'t exist: '.$dir);
+
+ }elseif( !is_writable($dir) ){
+ throw new Less_Exception_Parser('Less.php cache directory isn\'t writable: '.$dir);
+
+ }else{
+ $dir = self::WinPath($dir);
+ Less_Cache::$cache_dir = rtrim($dir,'/').'/';
+ return true;
+ }
+ }
+
+
+ /**
+ * Set a list of directories or callbacks the parser should use for determining import paths
+ *
+ * @param array $dirs
+ */
+ public function SetImportDirs( $dirs ){
+ Less_Parser::$options['import_dirs'] = array();
+
+ foreach($dirs as $path => $uri_root){
+
+ $path = self::WinPath($path);
+ if( !empty($path) ){
+ $path = rtrim($path,'/').'/';
+ }
+
+ if ( !is_callable($uri_root) ){
+ $uri_root = self::WinPath($uri_root);
+ if( !empty($uri_root) ){
+ $uri_root = rtrim($uri_root,'/').'/';
+ }
+ }
+
+ Less_Parser::$options['import_dirs'][$path] = $uri_root;
+ }
+ }
+
+ /**
+ * @param string $file_path
+ */
+ private function _parse( $file_path = null ){
+ $this->rules = array_merge($this->rules, $this->GetRules( $file_path ));
+ }
+
+
+ /**
+ * Return the results of parsePrimary for $file_path
+ * Use cache and save cached results if possible
+ *
+ * @param string|null $file_path
+ */
+ private function GetRules( $file_path ){
+
+ $this->SetInput($file_path);
+
+ $cache_file = $this->CacheFile( $file_path );
+ if( $cache_file ){
+ if( Less_Parser::$options['cache_method'] == 'callback' ){
+ if( is_callable(Less_Parser::$options['cache_callback_get']) ){
+ $cache = call_user_func_array(
+ Less_Parser::$options['cache_callback_get'],
+ array($this, $file_path, $cache_file)
+ );
+
+ if( $cache ){
+ $this->UnsetInput();
+ return $cache;
+ }
+ }
+
+ }elseif( file_exists($cache_file) ){
+ switch(Less_Parser::$options['cache_method']){
+
+ // Using serialize
+ // Faster but uses more memory
+ case 'serialize':
+ $cache = unserialize(file_get_contents($cache_file));
+ if( $cache ){
+ touch($cache_file);
+ $this->UnsetInput();
+ return $cache;
+ }
+ break;
+
+
+ // Using generated php code
+ case 'var_export':
+ case 'php':
+ $this->UnsetInput();
+ return include($cache_file);
+ }
+ }
+ }
+
+ $rules = $this->parsePrimary();
+
+ if( $this->pos < $this->input_len ){
+ throw new Less_Exception_Chunk($this->input, null, $this->furthest, $this->env->currentFileInfo);
+ }
+
+ $this->UnsetInput();
+
+
+ //save the cache
+ if( $cache_file ){
+ if( Less_Parser::$options['cache_method'] == 'callback' ){
+ if( is_callable(Less_Parser::$options['cache_callback_set']) ){
+ call_user_func_array(
+ Less_Parser::$options['cache_callback_set'],
+ array($this, $file_path, $cache_file, $rules)
+ );
+ }
+
+ }else{
+ //msg('write cache file');
+ switch(Less_Parser::$options['cache_method']){
+ case 'serialize':
+ file_put_contents( $cache_file, serialize($rules) );
+ break;
+ case 'php':
+ file_put_contents( $cache_file, '' );
+ break;
+ case 'var_export':
+ //Requires __set_state()
+ file_put_contents( $cache_file, '' );
+ break;
+ }
+
+ Less_Cache::CleanCache();
+ }
+ }
+
+ return $rules;
+ }
+
+
+ /**
+ * Set up the input buffer
+ *
+ */
+ public function SetInput( $file_path ){
+
+ if( $file_path ){
+ $this->input = file_get_contents( $file_path );
+ }
+
+ $this->pos = $this->furthest = 0;
+
+ // Remove potential UTF Byte Order Mark
+ $this->input = preg_replace('/\\G\xEF\xBB\xBF/', '', $this->input);
+ $this->input_len = strlen($this->input);
+
+
+ if( Less_Parser::$options['sourceMap'] && $this->env->currentFileInfo ){
+ $uri = $this->env->currentFileInfo['currentUri'];
+ Less_Parser::$contentsMap[$uri] = $this->input;
+ }
+
+ }
+
+
+ /**
+ * Free up some memory
+ *
+ */
+ public function UnsetInput(){
+ unset($this->input, $this->pos, $this->input_len, $this->furthest);
+ $this->saveStack = array();
+ }
+
+
+ public function CacheFile( $file_path ){
+
+ if( $file_path && $this->CacheEnabled() ){
+
+ $env = get_object_vars($this->env);
+ unset($env['frames']);
+
+ $parts = array();
+ $parts[] = $file_path;
+ $parts[] = filesize( $file_path );
+ $parts[] = filemtime( $file_path );
+ $parts[] = $env;
+ $parts[] = Less_Version::cache_version;
+ $parts[] = Less_Parser::$options['cache_method'];
+ return Less_Cache::$cache_dir . Less_Cache::$prefix . base_convert( sha1(json_encode($parts) ), 16, 36) . '.lesscache';
+ }
+ }
+
+
+ static function AddParsedFile($file){
+ self::$imports[] = $file;
+ }
+
+ static function AllParsedFiles(){
+ return self::$imports;
+ }
+
+ /**
+ * @param string $file
+ */
+ static function FileParsed($file){
+ return in_array($file,self::$imports);
+ }
+
+
+ function save() {
+ $this->saveStack[] = $this->pos;
+ }
+
+ private function restore() {
+ $this->pos = array_pop($this->saveStack);
+ }
+
+ private function forget(){
+ array_pop($this->saveStack);
+ }
+
+ /**
+ * Determine if the character at the specified offset from the current position is a white space.
+ *
+ * @param int $offset
+ *
+ * @return bool
+ */
+ private function isWhitespace($offset = 0) {
+ return strpos(" \t\n\r\v\f", $this->input[$this->pos + $offset]) !== false;
+ }
+
+ /**
+ * Parse from a token, regexp or string, and move forward if match
+ *
+ * @param array $toks
+ * @return array
+ */
+ private function match($toks){
+
+ // The match is confirmed, add the match length to `this::pos`,
+ // and consume any extra white-space characters (' ' || '\n')
+ // which come after that. The reason for this is that LeSS's
+ // grammar is mostly white-space insensitive.
+ //
+
+ foreach($toks as $tok){
+
+ $char = $tok[0];
+
+ if( $char === '/' ){
+ $match = $this->MatchReg($tok);
+
+ if( $match ){
+ return count($match) === 1 ? $match[0] : $match;
+ }
+
+ }elseif( $char === '#' ){
+ $match = $this->MatchChar($tok[1]);
+
+ }else{
+ // Non-terminal, match using a function call
+ $match = $this->$tok();
+
+ }
+
+ if( $match ){
+ return $match;
+ }
+ }
+ }
+
+ /**
+ * @param string[] $toks
+ *
+ * @return string
+ */
+ private function MatchFuncs($toks){
+
+ if( $this->pos < $this->input_len ){
+ foreach($toks as $tok){
+ $match = $this->$tok();
+ if( $match ){
+ return $match;
+ }
+ }
+ }
+
+ }
+
+ // Match a single character in the input,
+ private function MatchChar($tok){
+ if( ($this->pos < $this->input_len) && ($this->input[$this->pos] === $tok) ){
+ $this->skipWhitespace(1);
+ return $tok;
+ }
+ }
+
+ // Match a regexp from the current start point
+ private function MatchReg($tok){
+
+ if( preg_match($tok, $this->input, $match, 0, $this->pos) ){
+ $this->skipWhitespace(strlen($match[0]));
+ return $match;
+ }
+ }
+
+
+ /**
+ * Same as match(), but don't change the state of the parser,
+ * just return the match.
+ *
+ * @param string $tok
+ * @return integer
+ */
+ public function PeekReg($tok){
+ return preg_match($tok, $this->input, $match, 0, $this->pos);
+ }
+
+ /**
+ * @param string $tok
+ */
+ public function PeekChar($tok){
+ //return ($this->input[$this->pos] === $tok );
+ return ($this->pos < $this->input_len) && ($this->input[$this->pos] === $tok );
+ }
+
+
+ /**
+ * @param integer $length
+ */
+ public function skipWhitespace($length){
+
+ $this->pos += $length;
+
+ for(; $this->pos < $this->input_len; $this->pos++ ){
+ $c = $this->input[$this->pos];
+
+ if( ($c !== "\n") && ($c !== "\r") && ($c !== "\t") && ($c !== ' ') ){
+ break;
+ }
+ }
+ }
+
+
+ /**
+ * @param string $tok
+ * @param string|null $msg
+ */
+ public function expect($tok, $msg = NULL) {
+ $result = $this->match( array($tok) );
+ if (!$result) {
+ $this->Error( $msg ? "Expected '" . $tok . "' got '" . $this->input[$this->pos] . "'" : $msg );
+ } else {
+ return $result;
+ }
+ }
+
+ /**
+ * @param string $tok
+ */
+ public function expectChar($tok, $msg = null ){
+ $result = $this->MatchChar($tok);
+ if( !$result ){
+ $msg = $msg ? $msg : "Expected '" . $tok . "' got '" . $this->input[$this->pos] . "'";
+ $this->Error( $msg );
+ }else{
+ return $result;
+ }
+ }
+
+ //
+ // Here in, the parsing rules/functions
+ //
+ // The basic structure of the syntax tree generated is as follows:
+ //
+ // Ruleset -> Rule -> Value -> Expression -> Entity
+ //
+ // Here's some LESS code:
+ //
+ // .class {
+ // color: #fff;
+ // border: 1px solid #000;
+ // width: @w + 4px;
+ // > .child {...}
+ // }
+ //
+ // And here's what the parse tree might look like:
+ //
+ // Ruleset (Selector '.class', [
+ // Rule ("color", Value ([Expression [Color #fff]]))
+ // Rule ("border", Value ([Expression [Dimension 1px][Keyword "solid"][Color #000]]))
+ // Rule ("width", Value ([Expression [Operation "+" [Variable "@w"][Dimension 4px]]]))
+ // Ruleset (Selector [Element '>', '.child'], [...])
+ // ])
+ //
+ // In general, most rules will try to parse a token with the `$()` function, and if the return
+ // value is truly, will return a new node, of the relevant type. Sometimes, we need to check
+ // first, before parsing, that's when we use `peek()`.
+ //
+
+ //
+ // The `primary` rule is the *entry* and *exit* point of the parser.
+ // The rules here can appear at any level of the parse tree.
+ //
+ // The recursive nature of the grammar is an interplay between the `block`
+ // rule, which represents `{ ... }`, the `ruleset` rule, and this `primary` rule,
+ // as represented by this simplified grammar:
+ //
+ // primary → (ruleset | rule)+
+ // ruleset → selector+ block
+ // block → '{' primary '}'
+ //
+ // Only at one point is the primary rule not called from the
+ // block rule: at the root level.
+ //
+ private function parsePrimary(){
+ $root = array();
+
+ while( true ){
+
+ if( $this->pos >= $this->input_len ){
+ break;
+ }
+
+ $node = $this->parseExtend(true);
+ if( $node ){
+ $root = array_merge($root,$node);
+ continue;
+ }
+
+ //$node = $this->MatchFuncs( array( 'parseMixinDefinition', 'parseRule', 'parseRuleset', 'parseMixinCall', 'parseComment', 'parseDirective'));
+ $node = $this->MatchFuncs( array( 'parseMixinDefinition', 'parseNameValue', 'parseRule', 'parseRuleset', 'parseMixinCall', 'parseComment', 'parseRulesetCall', 'parseDirective'));
+
+ if( $node ){
+ $root[] = $node;
+ }elseif( !$this->MatchReg('/\\G[\s\n;]+/') ){
+ break;
+ }
+
+ if( $this->PeekChar('}') ){
+ break;
+ }
+ }
+
+ return $root;
+ }
+
+
+
+ // We create a Comment node for CSS comments `/* */`,
+ // but keep the LeSS comments `//` silent, by just skipping
+ // over them.
+ private function parseComment(){
+
+ if( $this->input[$this->pos] !== '/' ){
+ return;
+ }
+
+ if( $this->input[$this->pos+1] === '/' ){
+ $match = $this->MatchReg('/\\G\/\/.*/');
+ return $this->NewObj4('Less_Tree_Comment',array($match[0], true, $this->pos, $this->env->currentFileInfo));
+ }
+
+ //$comment = $this->MatchReg('/\\G\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/');
+ $comment = $this->MatchReg('/\\G\/\*(?s).*?\*+\/\n?/');//not the same as less.js to prevent fatal errors
+ if( $comment ){
+ return $this->NewObj4('Less_Tree_Comment',array($comment[0], false, $this->pos, $this->env->currentFileInfo));
+ }
+ }
+
+ private function parseComments(){
+ $comments = array();
+
+ while( $this->pos < $this->input_len ){
+ $comment = $this->parseComment();
+ if( !$comment ){
+ break;
+ }
+
+ $comments[] = $comment;
+ }
+
+ return $comments;
+ }
+
+
+
+ //
+ // A string, which supports escaping " and '
+ //
+ // "milky way" 'he\'s the one!'
+ //
+ private function parseEntitiesQuoted() {
+ $j = $this->pos;
+ $e = false;
+ $index = $this->pos;
+
+ if( $this->input[$this->pos] === '~' ){
+ $j++;
+ $e = true; // Escaped strings
+ }
+
+ $char = $this->input[$j];
+ if( $char !== '"' && $char !== "'" ){
+ return;
+ }
+
+ if ($e) {
+ $this->MatchChar('~');
+ }
+
+
+ $matched = $this->MatchQuoted($char, $j+1);
+ if( $matched === false ){
+ return;
+ }
+
+ $quoted = $char.$matched.$char;
+ return $this->NewObj5('Less_Tree_Quoted',array($quoted, $matched, $e, $index, $this->env->currentFileInfo) );
+ }
+
+
+ /**
+ * When PCRE JIT is enabled in php, regular expressions don't work for matching quoted strings
+ *
+ * $regex = '/\\G\'((?:[^\'\\\\\r\n]|\\\\.|\\\\\r\n|\\\\[\n\r\f])*)\'/';
+ * $regex = '/\\G"((?:[^"\\\\\r\n]|\\\\.|\\\\\r\n|\\\\[\n\r\f])*)"/';
+ *
+ */
+ private function MatchQuoted($quote_char, $i){
+
+ $matched = '';
+ while( $i < $this->input_len ){
+ $c = $this->input[$i];
+
+ //escaped character
+ if( $c === '\\' ){
+ $matched .= $c . $this->input[$i+1];
+ $i += 2;
+ continue;
+ }
+
+ if( $c === $quote_char ){
+ $this->pos = $i+1;
+ $this->skipWhitespace(0);
+ return $matched;
+ }
+
+ if( $c === "\r" || $c === "\n" ){
+ return false;
+ }
+
+ $i++;
+ $matched .= $c;
+ }
+
+ return false;
+ }
+
+
+ //
+ // A catch-all word, such as:
+ //
+ // black border-collapse
+ //
+ private function parseEntitiesKeyword(){
+
+ //$k = $this->MatchReg('/\\G[_A-Za-z-][_A-Za-z0-9-]*/');
+ $k = $this->MatchReg('/\\G%|\\G[_A-Za-z-][_A-Za-z0-9-]*/');
+ if( $k ){
+ $k = $k[0];
+ $color = $this->fromKeyword($k);
+ if( $color ){
+ return $color;
+ }
+ return $this->NewObj1('Less_Tree_Keyword',$k);
+ }
+ }
+
+ // duplicate of Less_Tree_Color::FromKeyword
+ private function FromKeyword( $keyword ){
+ $keyword = strtolower($keyword);
+
+ if( Less_Colors::hasOwnProperty($keyword) ){
+ // detect named color
+ return $this->NewObj1('Less_Tree_Color',substr(Less_Colors::color($keyword), 1));
+ }
+
+ if( $keyword === 'transparent' ){
+ return $this->NewObj3('Less_Tree_Color', array( array(0, 0, 0), 0, true));
+ }
+ }
+
+ //
+ // A function call
+ //
+ // rgb(255, 0, 255)
+ //
+ // We also try to catch IE's `alpha()`, but let the `alpha` parser
+ // deal with the details.
+ //
+ // The arguments are parsed with the `entities.arguments` parser.
+ //
+ private function parseEntitiesCall(){
+ $index = $this->pos;
+
+ if( !preg_match('/\\G([\w-]+|%|progid:[\w\.]+)\(/', $this->input, $name,0,$this->pos) ){
+ return;
+ }
+ $name = $name[1];
+ $nameLC = strtolower($name);
+
+ if ($nameLC === 'url') {
+ return null;
+ }
+
+ $this->pos += strlen($name);
+
+ if( $nameLC === 'alpha' ){
+ $alpha_ret = $this->parseAlpha();
+ if( $alpha_ret ){
+ return $alpha_ret;
+ }
+ }
+
+ $this->MatchChar('('); // Parse the '(' and consume whitespace.
+
+ $args = $this->parseEntitiesArguments();
+
+ if( !$this->MatchChar(')') ){
+ return;
+ }
+
+ if ($name) {
+ return $this->NewObj4('Less_Tree_Call',array($name, $args, $index, $this->env->currentFileInfo) );
+ }
+ }
+
+ /**
+ * Parse a list of arguments
+ *
+ * @return array
+ */
+ private function parseEntitiesArguments(){
+
+ $args = array();
+ while( true ){
+ $arg = $this->MatchFuncs( array('parseEntitiesAssignment','parseExpression') );
+ if( !$arg ){
+ break;
+ }
+
+ $args[] = $arg;
+ if( !$this->MatchChar(',') ){
+ break;
+ }
+ }
+ return $args;
+ }
+
+ private function parseEntitiesLiteral(){
+ return $this->MatchFuncs( array('parseEntitiesDimension','parseEntitiesColor','parseEntitiesQuoted','parseUnicodeDescriptor') );
+ }
+
+ // Assignments are argument entities for calls.
+ // They are present in ie filter properties as shown below.
+ //
+ // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* )
+ //
+ private function parseEntitiesAssignment() {
+
+ $key = $this->MatchReg('/\\G\w+(?=\s?=)/');
+ if( !$key ){
+ return;
+ }
+
+ if( !$this->MatchChar('=') ){
+ return;
+ }
+
+ $value = $this->parseEntity();
+ if( $value ){
+ return $this->NewObj2('Less_Tree_Assignment',array($key[0], $value));
+ }
+ }
+
+ //
+ // Parse url() tokens
+ //
+ // We use a specific rule for urls, because they don't really behave like
+ // standard function calls. The difference is that the argument doesn't have
+ // to be enclosed within a string, so it can't be parsed as an Expression.
+ //
+ private function parseEntitiesUrl(){
+
+
+ if( $this->input[$this->pos] !== 'u' || !$this->matchReg('/\\Gurl\(/') ){
+ return;
+ }
+
+ $value = $this->match( array('parseEntitiesQuoted','parseEntitiesVariable','/\\Gdata\:.*?[^\)]+/','/\\G(?:(?:\\\\[\(\)\'"])|[^\(\)\'"])+/') );
+ if( !$value ){
+ $value = '';
+ }
+
+
+ $this->expectChar(')');
+
+
+ if( isset($value->value) || $value instanceof Less_Tree_Variable ){
+ return $this->NewObj2('Less_Tree_Url',array($value, $this->env->currentFileInfo));
+ }
+
+ return $this->NewObj2('Less_Tree_Url', array( $this->NewObj1('Less_Tree_Anonymous',$value), $this->env->currentFileInfo) );
+ }
+
+
+ //
+ // A Variable entity, such as `@fink`, in
+ //
+ // width: @fink + 2px
+ //
+ // We use a different parser for variable definitions,
+ // see `parsers.variable`.
+ //
+ private function parseEntitiesVariable(){
+ $index = $this->pos;
+ if ($this->PeekChar('@') && ($name = $this->MatchReg('/\\G@@?[\w-]+/'))) {
+ return $this->NewObj3('Less_Tree_Variable', array( $name[0], $index, $this->env->currentFileInfo));
+ }
+ }
+
+
+ // A variable entity using the protective {} e.g. @{var}
+ private function parseEntitiesVariableCurly() {
+ $index = $this->pos;
+
+ if( $this->input_len > ($this->pos+1) && $this->input[$this->pos] === '@' && ($curly = $this->MatchReg('/\\G@\{([\w-]+)\}/')) ){
+ return $this->NewObj3('Less_Tree_Variable',array('@'.$curly[1], $index, $this->env->currentFileInfo));
+ }
+ }
+
+ //
+ // A Hexadecimal color
+ //
+ // #4F3C2F
+ //
+ // `rgb` and `hsl` colors are parsed through the `entities.call` parser.
+ //
+ private function parseEntitiesColor(){
+ if ($this->PeekChar('#') && ($rgb = $this->MatchReg('/\\G#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/'))) {
+ return $this->NewObj1('Less_Tree_Color',$rgb[1]);
+ }
+ }
+
+ //
+ // A Dimension, that is, a number and a unit
+ //
+ // 0.5em 95%
+ //
+ private function parseEntitiesDimension(){
+
+ $c = @ord($this->input[$this->pos]);
+
+ //Is the first char of the dimension 0-9, '.', '+' or '-'
+ if (($c > 57 || $c < 43) || $c === 47 || $c == 44){
+ return;
+ }
+
+ $value = $this->MatchReg('/\\G([+-]?\d*\.?\d+)(%|[a-z]+)?/');
+ if( $value ){
+
+ if( isset($value[2]) ){
+ return $this->NewObj2('Less_Tree_Dimension', array($value[1],$value[2]));
+ }
+ return $this->NewObj1('Less_Tree_Dimension',$value[1]);
+ }
+ }
+
+
+ //
+ // A unicode descriptor, as is used in unicode-range
+ //
+ // U+0?? or U+00A1-00A9
+ //
+ function parseUnicodeDescriptor() {
+ $ud = $this->MatchReg('/\\G(U\+[0-9a-fA-F?]+)(\-[0-9a-fA-F?]+)?/');
+ if( $ud ){
+ return $this->NewObj1('Less_Tree_UnicodeDescriptor', $ud[0]);
+ }
+ }
+
+
+ //
+ // JavaScript code to be evaluated
+ //
+ // `window.location.href`
+ //
+ private function parseEntitiesJavascript(){
+ $e = false;
+ $j = $this->pos;
+ if( $this->input[$j] === '~' ){
+ $j++;
+ $e = true;
+ }
+ if( $this->input[$j] !== '`' ){
+ return;
+ }
+ if( $e ){
+ $this->MatchChar('~');
+ }
+ $str = $this->MatchReg('/\\G`([^`]*)`/');
+ if( $str ){
+ return $this->NewObj3('Less_Tree_Javascript', array($str[1], $this->pos, $e));
+ }
+ }
+
+
+ //
+ // The variable part of a variable definition. Used in the `rule` parser
+ //
+ // @fink:
+ //
+ private function parseVariable(){
+ if ($this->PeekChar('@') && ($name = $this->MatchReg('/\\G(@[\w-]+)\s*:/'))) {
+ return $name[1];
+ }
+ }
+
+
+ //
+ // The variable part of a variable definition. Used in the `rule` parser
+ //
+ // @fink();
+ //
+ private function parseRulesetCall(){
+
+ if( $this->input[$this->pos] === '@' && ($name = $this->MatchReg('/\\G(@[\w-]+)\s*\(\s*\)\s*;/')) ){
+ return $this->NewObj1('Less_Tree_RulesetCall', $name[1] );
+ }
+ }
+
+
+ //
+ // extend syntax - used to extend selectors
+ //
+ function parseExtend($isRule = false){
+
+ $index = $this->pos;
+ $extendList = array();
+
+
+ if( !$this->MatchReg( $isRule ? '/\\G&:extend\(/' : '/\\G:extend\(/' ) ){ return; }
+
+ do{
+ $option = null;
+ $elements = array();
+ while( true ){
+ $option = $this->MatchReg('/\\G(all)(?=\s*(\)|,))/');
+ if( $option ){ break; }
+ $e = $this->parseElement();
+ if( !$e ){ break; }
+ $elements[] = $e;
+ }
+
+ if( $option ){
+ $option = $option[1];
+ }
+
+ $extendList[] = $this->NewObj3('Less_Tree_Extend', array( $this->NewObj1('Less_Tree_Selector',$elements), $option, $index ));
+
+ }while( $this->MatchChar(",") );
+
+ $this->expect('/\\G\)/');
+
+ if( $isRule ){
+ $this->expect('/\\G;/');
+ }
+
+ return $extendList;
+ }
+
+
+ //
+ // A Mixin call, with an optional argument list
+ //
+ // #mixins > .square(#fff);
+ // .rounded(4px, black);
+ // .button;
+ //
+ // The `while` loop is there because mixins can be
+ // namespaced, but we only support the child and descendant
+ // selector for now.
+ //
+ private function parseMixinCall(){
+
+ $char = $this->input[$this->pos];
+ if( $char !== '.' && $char !== '#' ){
+ return;
+ }
+
+ $index = $this->pos;
+ $this->save(); // stop us absorbing part of an invalid selector
+
+ $elements = $this->parseMixinCallElements();
+
+ if( $elements ){
+
+ if( $this->MatchChar('(') ){
+ $returned = $this->parseMixinArgs(true);
+ $args = $returned['args'];
+ $this->expectChar(')');
+ }else{
+ $args = array();
+ }
+
+ $important = $this->parseImportant();
+
+ if( $this->parseEnd() ){
+ $this->forget();
+ return $this->NewObj5('Less_Tree_Mixin_Call', array( $elements, $args, $index, $this->env->currentFileInfo, $important));
+ }
+ }
+
+ $this->restore();
+ }
+
+
+ private function parseMixinCallElements(){
+ $elements = array();
+ $c = null;
+
+ while( true ){
+ $elemIndex = $this->pos;
+ $e = $this->MatchReg('/\\G[#.](?:[\w-]|\\\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/');
+ if( !$e ){
+ break;
+ }
+ $elements[] = $this->NewObj4('Less_Tree_Element', array($c, $e[0], $elemIndex, $this->env->currentFileInfo));
+ $c = $this->MatchChar('>');
+ }
+
+ return $elements;
+ }
+
+
+
+ /**
+ * @param boolean $isCall
+ */
+ private function parseMixinArgs( $isCall ){
+ $expressions = array();
+ $argsSemiColon = array();
+ $isSemiColonSeperated = null;
+ $argsComma = array();
+ $expressionContainsNamed = null;
+ $name = null;
+ $returner = array('args'=>array(), 'variadic'=> false);
+
+ $this->save();
+
+ while( true ){
+ if( $isCall ){
+ $arg = $this->MatchFuncs( array( 'parseDetachedRuleset','parseExpression' ) );
+ } else {
+ $this->parseComments();
+ if( $this->input[ $this->pos ] === '.' && $this->MatchReg('/\\G\.{3}/') ){
+ $returner['variadic'] = true;
+ if( $this->MatchChar(";") && !$isSemiColonSeperated ){
+ $isSemiColonSeperated = true;
+ }
+
+ if( $isSemiColonSeperated ){
+ $argsSemiColon[] = array('variadic'=>true);
+ }else{
+ $argsComma[] = array('variadic'=>true);
+ }
+ break;
+ }
+ $arg = $this->MatchFuncs( array('parseEntitiesVariable','parseEntitiesLiteral','parseEntitiesKeyword') );
+ }
+
+ if( !$arg ){
+ break;
+ }
+
+
+ $nameLoop = null;
+ if( $arg instanceof Less_Tree_Expression ){
+ $arg->throwAwayComments();
+ }
+ $value = $arg;
+ $val = null;
+
+ if( $isCall ){
+ // Variable
+ if( property_exists($arg,'value') && count($arg->value) == 1 ){
+ $val = $arg->value[0];
+ }
+ } else {
+ $val = $arg;
+ }
+
+
+ if( $val instanceof Less_Tree_Variable ){
+
+ if( $this->MatchChar(':') ){
+ if( $expressions ){
+ if( $isSemiColonSeperated ){
+ $this->Error('Cannot mix ; and , as delimiter types');
+ }
+ $expressionContainsNamed = true;
+ }
+
+ // we do not support setting a ruleset as a default variable - it doesn't make sense
+ // However if we do want to add it, there is nothing blocking it, just don't error
+ // and remove isCall dependency below
+ $value = null;
+ if( $isCall ){
+ $value = $this->parseDetachedRuleset();
+ }
+ if( !$value ){
+ $value = $this->parseExpression();
+ }
+
+ if( !$value ){
+ if( $isCall ){
+ $this->Error('could not understand value for named argument');
+ } else {
+ $this->restore();
+ $returner['args'] = array();
+ return $returner;
+ }
+ }
+
+ $nameLoop = ($name = $val->name);
+ }elseif( !$isCall && $this->MatchReg('/\\G\.{3}/') ){
+ $returner['variadic'] = true;
+ if( $this->MatchChar(";") && !$isSemiColonSeperated ){
+ $isSemiColonSeperated = true;
+ }
+ if( $isSemiColonSeperated ){
+ $argsSemiColon[] = array('name'=> $arg->name, 'variadic' => true);
+ }else{
+ $argsComma[] = array('name'=> $arg->name, 'variadic' => true);
+ }
+ break;
+ }elseif( !$isCall ){
+ $name = $nameLoop = $val->name;
+ $value = null;
+ }
+ }
+
+ if( $value ){
+ $expressions[] = $value;
+ }
+
+ $argsComma[] = array('name'=>$nameLoop, 'value'=>$value );
+
+ if( $this->MatchChar(',') ){
+ continue;
+ }
+
+ if( $this->MatchChar(';') || $isSemiColonSeperated ){
+
+ if( $expressionContainsNamed ){
+ $this->Error('Cannot mix ; and , as delimiter types');
+ }
+
+ $isSemiColonSeperated = true;
+
+ if( count($expressions) > 1 ){
+ $value = $this->NewObj1('Less_Tree_Value', $expressions);
+ }
+ $argsSemiColon[] = array('name'=>$name, 'value'=>$value );
+
+ $name = null;
+ $expressions = array();
+ $expressionContainsNamed = false;
+ }
+ }
+
+ $this->forget();
+ $returner['args'] = ($isSemiColonSeperated ? $argsSemiColon : $argsComma);
+ return $returner;
+ }
+
+
+
+ //
+ // A Mixin definition, with a list of parameters
+ //
+ // .rounded (@radius: 2px, @color) {
+ // ...
+ // }
+ //
+ // Until we have a finer grained state-machine, we have to
+ // do a look-ahead, to make sure we don't have a mixin call.
+ // See the `rule` function for more information.
+ //
+ // We start by matching `.rounded (`, and then proceed on to
+ // the argument list, which has optional default values.
+ // We store the parameters in `params`, with a `value` key,
+ // if there is a value, such as in the case of `@radius`.
+ //
+ // Once we've got our params list, and a closing `)`, we parse
+ // the `{...}` block.
+ //
+ private function parseMixinDefinition(){
+ $cond = null;
+
+ $char = $this->input[$this->pos];
+ if( ($char !== '.' && $char !== '#') || ($char === '{' && $this->PeekReg('/\\G[^{]*\}/')) ){
+ return;
+ }
+
+ $this->save();
+
+ $match = $this->MatchReg('/\\G([#.](?:[\w-]|\\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/');
+ if( $match ){
+ $name = $match[1];
+
+ $argInfo = $this->parseMixinArgs( false );
+ $params = $argInfo['args'];
+ $variadic = $argInfo['variadic'];
+
+
+ // .mixincall("@{a}");
+ // looks a bit like a mixin definition..
+ // also
+ // .mixincall(@a: {rule: set;});
+ // so we have to be nice and restore
+ if( !$this->MatchChar(')') ){
+ $this->furthest = $this->pos;
+ $this->restore();
+ return;
+ }
+
+
+ $this->parseComments();
+
+ if ($this->MatchReg('/\\Gwhen/')) { // Guard
+ $cond = $this->expect('parseConditions', 'Expected conditions');
+ }
+
+ $ruleset = $this->parseBlock();
+
+ if( is_array($ruleset) ){
+ $this->forget();
+ return $this->NewObj5('Less_Tree_Mixin_Definition', array( $name, $params, $ruleset, $cond, $variadic));
+ }
+
+ $this->restore();
+ }else{
+ $this->forget();
+ }
+ }
+
+ //
+ // Entities are the smallest recognized token,
+ // and can be found inside a rule's value.
+ //
+ private function parseEntity(){
+
+ return $this->MatchFuncs( array('parseEntitiesLiteral','parseEntitiesVariable','parseEntitiesUrl','parseEntitiesCall','parseEntitiesKeyword','parseEntitiesJavascript','parseComment') );
+ }
+
+ //
+ // A Rule terminator. Note that we use `peek()` to check for '}',
+ // because the `block` rule will be expecting it, but we still need to make sure
+ // it's there, if ';' was omitted.
+ //
+ private function parseEnd(){
+ return $this->MatchChar(';') || $this->PeekChar('}');
+ }
+
+ //
+ // IE's alpha function
+ //
+ // alpha(opacity=88)
+ //
+ private function parseAlpha(){
+
+ if ( ! $this->MatchReg('/\\G\(opacity=/i')) {
+ return;
+ }
+
+ $value = $this->MatchReg('/\\G[0-9]+/');
+ if( $value ){
+ $value = $value[0];
+ }else{
+ $value = $this->parseEntitiesVariable();
+ if( !$value ){
+ return;
+ }
+ }
+
+ $this->expectChar(')');
+ return $this->NewObj1('Less_Tree_Alpha',$value);
+ }
+
+
+ //
+ // A Selector Element
+ //
+ // div
+ // + h1
+ // #socks
+ // input[type="text"]
+ //
+ // Elements are the building blocks for Selectors,
+ // they are made out of a `Combinator` (see combinator rule),
+ // and an element name, such as a tag a class, or `*`.
+ //
+ private function parseElement(){
+ $c = $this->parseCombinator();
+ $index = $this->pos;
+
+ $e = $this->match( array('/\\G(?:\d+\.\d+|\d+)%/', '/\\G(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/',
+ '#*', '#&', 'parseAttribute', '/\\G\([^()@]+\)/', '/\\G[\.#](?=@)/', 'parseEntitiesVariableCurly') );
+
+ if( is_null($e) ){
+ $this->save();
+ if( $this->MatchChar('(') ){
+ if( ($v = $this->parseSelector()) && $this->MatchChar(')') ){
+ $e = $this->NewObj1('Less_Tree_Paren',$v);
+ $this->forget();
+ }else{
+ $this->restore();
+ }
+ }else{
+ $this->forget();
+ }
+ }
+
+ if( !is_null($e) ){
+ return $this->NewObj4('Less_Tree_Element',array( $c, $e, $index, $this->env->currentFileInfo));
+ }
+ }
+
+ //
+ // Combinators combine elements together, in a Selector.
+ //
+ // Because our parser isn't white-space sensitive, special care
+ // has to be taken, when parsing the descendant combinator, ` `,
+ // as it's an empty space. We have to check the previous character
+ // in the input, to see if it's a ` ` character.
+ //
+ private function parseCombinator(){
+ if( $this->pos < $this->input_len ){
+ $c = $this->input[$this->pos];
+ if ($c === '>' || $c === '+' || $c === '~' || $c === '|' || $c === '^' ){
+
+ $this->pos++;
+ if( $this->input[$this->pos] === '^' ){
+ $c = '^^';
+ $this->pos++;
+ }
+
+ $this->skipWhitespace(0);
+
+ return $c;
+ }
+
+ if( $this->pos > 0 && $this->isWhitespace(-1) ){
+ return ' ';
+ }
+ }
+ }
+
+ //
+ // A CSS selector (see selector below)
+ // with less extensions e.g. the ability to extend and guard
+ //
+ private function parseLessSelector(){
+ return $this->parseSelector(true);
+ }
+
+ //
+ // A CSS Selector
+ //
+ // .class > div + h1
+ // li a:hover
+ //
+ // Selectors are made out of one or more Elements, see above.
+ //
+ private function parseSelector( $isLess = false ){
+ $elements = array();
+ $extendList = array();
+ $condition = null;
+ $when = false;
+ $extend = false;
+ $e = null;
+ $c = null;
+ $index = $this->pos;
+
+ while( ($isLess && ($extend = $this->parseExtend())) || ($isLess && ($when = $this->MatchReg('/\\Gwhen/') )) || ($e = $this->parseElement()) ){
+ if( $when ){
+ $condition = $this->expect('parseConditions', 'expected condition');
+ }elseif( $condition ){
+ //error("CSS guard can only be used at the end of selector");
+ }elseif( $extend ){
+ $extendList = array_merge($extendList,$extend);
+ }else{
+ //if( count($extendList) ){
+ //error("Extend can only be used at the end of selector");
+ //}
+ if( $this->pos < $this->input_len ){
+ $c = $this->input[ $this->pos ];
+ }
+ $elements[] = $e;
+ $e = null;
+ }
+
+ if( $c === '{' || $c === '}' || $c === ';' || $c === ',' || $c === ')') { break; }
+ }
+
+ if( $elements ){
+ return $this->NewObj5('Less_Tree_Selector',array($elements, $extendList, $condition, $index, $this->env->currentFileInfo));
+ }
+ if( $extendList ) {
+ $this->Error('Extend must be used to extend a selector, it cannot be used on its own');
+ }
+ }
+
+ private function parseTag(){
+ return ( $tag = $this->MatchReg('/\\G[A-Za-z][A-Za-z-]*[0-9]?/') ) ? $tag : $this->MatchChar('*');
+ }
+
+ private function parseAttribute(){
+
+ $val = null;
+
+ if( !$this->MatchChar('[') ){
+ return;
+ }
+
+ $key = $this->parseEntitiesVariableCurly();
+ if( !$key ){
+ $key = $this->expect('/\\G(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\\\.)+/');
+ }
+
+ $op = $this->MatchReg('/\\G[|~*$^]?=/');
+ if( $op ){
+ $val = $this->match( array('parseEntitiesQuoted','/\\G[0-9]+%/','/\\G[\w-]+/','parseEntitiesVariableCurly') );
+ }
+
+ $this->expectChar(']');
+
+ return $this->NewObj3('Less_Tree_Attribute',array( $key, $op[0], $val));
+ }
+
+ //
+ // The `block` rule is used by `ruleset` and `mixin.definition`.
+ // It's a wrapper around the `primary` rule, with added `{}`.
+ //
+ private function parseBlock(){
+ if( $this->MatchChar('{') ){
+ $content = $this->parsePrimary();
+ if( $this->MatchChar('}') ){
+ return $content;
+ }
+ }
+ }
+
+ private function parseBlockRuleset(){
+ $block = $this->parseBlock();
+
+ if( $block ){
+ $block = $this->NewObj2('Less_Tree_Ruleset',array( null, $block));
+ }
+
+ return $block;
+ }
+
+ private function parseDetachedRuleset(){
+ $blockRuleset = $this->parseBlockRuleset();
+ if( $blockRuleset ){
+ return $this->NewObj1('Less_Tree_DetachedRuleset',$blockRuleset);
+ }
+ }
+
+ //
+ // div, .class, body > p {...}
+ //
+ private function parseRuleset(){
+ $selectors = array();
+
+ $this->save();
+
+ while( true ){
+ $s = $this->parseLessSelector();
+ if( !$s ){
+ break;
+ }
+ $selectors[] = $s;
+ $this->parseComments();
+
+ if( $s->condition && count($selectors) > 1 ){
+ $this->Error('Guards are only currently allowed on a single selector.');
+ }
+
+ if( !$this->MatchChar(',') ){
+ break;
+ }
+ if( $s->condition ){
+ $this->Error('Guards are only currently allowed on a single selector.');
+ }
+ $this->parseComments();
+ }
+
+
+ if( $selectors ){
+ $rules = $this->parseBlock();
+ if( is_array($rules) ){
+ $this->forget();
+ return $this->NewObj2('Less_Tree_Ruleset',array( $selectors, $rules)); //Less_Environment::$strictImports
+ }
+ }
+
+ // Backtrack
+ $this->furthest = $this->pos;
+ $this->restore();
+ }
+
+ /**
+ * Custom less.php parse function for finding simple name-value css pairs
+ * ex: width:100px;
+ *
+ */
+ private function parseNameValue(){
+
+ $index = $this->pos;
+ $this->save();
+
+
+ //$match = $this->MatchReg('/\\G([a-zA-Z\-]+)\s*:\s*((?:\'")?[a-zA-Z0-9\-% \.,!]+?(?:\'")?)\s*([;}])/');
+ $match = $this->MatchReg('/\\G([a-zA-Z\-]+)\s*:\s*([\'"]?[#a-zA-Z0-9\-%\.,]+?[\'"]?) *(! *important)?\s*([;}])/');
+ if( $match ){
+
+ if( $match[4] == '}' ){
+ $this->pos = $index + strlen($match[0])-1;
+ }
+
+ if( $match[3] ){
+ $match[2] .= ' !important';
+ }
+
+ return $this->NewObj4('Less_Tree_NameValue',array( $match[1], $match[2], $index, $this->env->currentFileInfo));
+ }
+
+ $this->restore();
+ }
+
+
+ private function parseRule( $tryAnonymous = null ){
+
+ $merge = false;
+ $startOfRule = $this->pos;
+
+ $c = $this->input[$this->pos];
+ if( $c === '.' || $c === '#' || $c === '&' ){
+ return;
+ }
+
+ $this->save();
+ $name = $this->MatchFuncs( array('parseVariable','parseRuleProperty'));
+
+ if( $name ){
+
+ $isVariable = is_string($name);
+
+ $value = null;
+ if( $isVariable ){
+ $value = $this->parseDetachedRuleset();
+ }
+
+ $important = null;
+ if( !$value ){
+
+ // prefer to try to parse first if its a variable or we are compressing
+ // but always fallback on the other one
+ //if( !$tryAnonymous && is_string($name) && $name[0] === '@' ){
+ if( !$tryAnonymous && (Less_Parser::$options['compress'] || $isVariable) ){
+ $value = $this->MatchFuncs( array('parseValue','parseAnonymousValue'));
+ }else{
+ $value = $this->MatchFuncs( array('parseAnonymousValue','parseValue'));
+ }
+
+ $important = $this->parseImportant();
+
+ // a name returned by this.ruleProperty() is always an array of the form:
+ // [string-1, ..., string-n, ""] or [string-1, ..., string-n, "+"]
+ // where each item is a tree.Keyword or tree.Variable
+ if( !$isVariable && is_array($name) ){
+ $nm = array_pop($name);
+ if( $nm->value ){
+ $merge = $nm->value;
+ }
+ }
+ }
+
+
+ if( $value && $this->parseEnd() ){
+ $this->forget();
+ return $this->NewObj6('Less_Tree_Rule',array( $name, $value, $important, $merge, $startOfRule, $this->env->currentFileInfo));
+ }else{
+ $this->furthest = $this->pos;
+ $this->restore();
+ if( $value && !$tryAnonymous ){
+ return $this->parseRule(true);
+ }
+ }
+ }else{
+ $this->forget();
+ }
+ }
+
+ function parseAnonymousValue(){
+
+ if( preg_match('/\\G([^@+\/\'"*`(;{}-]*);/',$this->input, $match, 0, $this->pos) ){
+ $this->pos += strlen($match[1]);
+ return $this->NewObj1('Less_Tree_Anonymous',$match[1]);
+ }
+ }
+
+ //
+ // An @import directive
+ //
+ // @import "lib";
+ //
+ // Depending on our environment, importing is done differently:
+ // In the browser, it's an XHR request, in Node, it would be a
+ // file-system operation. The function used for importing is
+ // stored in `import`, which we pass to the Import constructor.
+ //
+ private function parseImport(){
+
+ $this->save();
+
+ $dir = $this->MatchReg('/\\G@import?\s+/');
+
+ if( $dir ){
+ $options = $this->parseImportOptions();
+ $path = $this->MatchFuncs( array('parseEntitiesQuoted','parseEntitiesUrl'));
+
+ if( $path ){
+ $features = $this->parseMediaFeatures();
+ if( $this->MatchChar(';') ){
+ if( $features ){
+ $features = $this->NewObj1('Less_Tree_Value',$features);
+ }
+
+ $this->forget();
+ return $this->NewObj5('Less_Tree_Import',array( $path, $features, $options, $this->pos, $this->env->currentFileInfo));
+ }
+ }
+ }
+
+ $this->restore();
+ }
+
+ private function parseImportOptions(){
+
+ $options = array();
+
+ // list of options, surrounded by parens
+ if( !$this->MatchChar('(') ){
+ return $options;
+ }
+ do{
+ $optionName = $this->parseImportOption();
+ if( $optionName ){
+ $value = true;
+ switch( $optionName ){
+ case "css":
+ $optionName = "less";
+ $value = false;
+ break;
+ case "once":
+ $optionName = "multiple";
+ $value = false;
+ break;
+ }
+ $options[$optionName] = $value;
+ if( !$this->MatchChar(',') ){ break; }
+ }
+ }while( $optionName );
+ $this->expectChar(')');
+ return $options;
+ }
+
+ private function parseImportOption(){
+ $opt = $this->MatchReg('/\\G(less|css|multiple|once|inline|reference|optional)/');
+ if( $opt ){
+ return $opt[1];
+ }
+ }
+
+ private function parseMediaFeature() {
+ $nodes = array();
+
+ do{
+ $e = $this->MatchFuncs(array('parseEntitiesKeyword','parseEntitiesVariable'));
+ if( $e ){
+ $nodes[] = $e;
+ } elseif ($this->MatchChar('(')) {
+ $p = $this->parseProperty();
+ $e = $this->parseValue();
+ if ($this->MatchChar(')')) {
+ if ($p && $e) {
+ $r = $this->NewObj7('Less_Tree_Rule', array( $p, $e, null, null, $this->pos, $this->env->currentFileInfo, true));
+ $nodes[] = $this->NewObj1('Less_Tree_Paren',$r);
+ } elseif ($e) {
+ $nodes[] = $this->NewObj1('Less_Tree_Paren',$e);
+ } else {
+ return null;
+ }
+ } else
+ return null;
+ }
+ } while ($e);
+
+ if ($nodes) {
+ return $this->NewObj1('Less_Tree_Expression',$nodes);
+ }
+ }
+
+ private function parseMediaFeatures() {
+ $features = array();
+
+ do{
+ $e = $this->parseMediaFeature();
+ if( $e ){
+ $features[] = $e;
+ if (!$this->MatchChar(',')) break;
+ }else{
+ $e = $this->parseEntitiesVariable();
+ if( $e ){
+ $features[] = $e;
+ if (!$this->MatchChar(',')) break;
+ }
+ }
+ } while ($e);
+
+ return $features ? $features : null;
+ }
+
+ private function parseMedia() {
+ if( $this->MatchReg('/\\G@media/') ){
+ $features = $this->parseMediaFeatures();
+ $rules = $this->parseBlock();
+
+ if( is_array($rules) ){
+ return $this->NewObj4('Less_Tree_Media',array( $rules, $features, $this->pos, $this->env->currentFileInfo));
+ }
+ }
+ }
+
+
+ //
+ // A CSS Directive
+ //
+ // @charset "utf-8";
+ //
+ private function parseDirective(){
+
+ if( !$this->PeekChar('@') ){
+ return;
+ }
+
+ $rules = null;
+ $index = $this->pos;
+ $hasBlock = true;
+ $hasIdentifier = false;
+ $hasExpression = false;
+ $hasUnknown = false;
+
+
+ $value = $this->MatchFuncs(array('parseImport','parseMedia'));
+ if( $value ){
+ return $value;
+ }
+
+ $this->save();
+
+ $name = $this->MatchReg('/\\G@[a-z-]+/');
+
+ if( !$name ) return;
+ $name = $name[0];
+
+
+ $nonVendorSpecificName = $name;
+ $pos = strpos($name,'-', 2);
+ if( $name[1] == '-' && $pos > 0 ){
+ $nonVendorSpecificName = "@" . substr($name, $pos + 1);
+ }
+
+
+ switch( $nonVendorSpecificName ){
+ /*
+ case "@font-face":
+ case "@viewport":
+ case "@top-left":
+ case "@top-left-corner":
+ case "@top-center":
+ case "@top-right":
+ case "@top-right-corner":
+ case "@bottom-left":
+ case "@bottom-left-corner":
+ case "@bottom-center":
+ case "@bottom-right":
+ case "@bottom-right-corner":
+ case "@left-top":
+ case "@left-middle":
+ case "@left-bottom":
+ case "@right-top":
+ case "@right-middle":
+ case "@right-bottom":
+ hasBlock = true;
+ break;
+ */
+ case "@charset":
+ $hasIdentifier = true;
+ $hasBlock = false;
+ break;
+ case "@namespace":
+ $hasExpression = true;
+ $hasBlock = false;
+ break;
+ case "@keyframes":
+ $hasIdentifier = true;
+ break;
+ case "@host":
+ case "@page":
+ case "@document":
+ case "@supports":
+ $hasUnknown = true;
+ break;
+ }
+
+ if( $hasIdentifier ){
+ $value = $this->parseEntity();
+ if( !$value ){
+ $this->error("expected " . $name . " identifier");
+ }
+ } else if( $hasExpression ){
+ $value = $this->parseExpression();
+ if( !$value ){
+ $this->error("expected " . $name. " expression");
+ }
+ } else if ($hasUnknown) {
+
+ $value = $this->MatchReg('/\\G[^{;]+/');
+ if( $value ){
+ $value = $this->NewObj1('Less_Tree_Anonymous',trim($value[0]));
+ }
+ }
+
+ if( $hasBlock ){
+ $rules = $this->parseBlockRuleset();
+ }
+
+ if( $rules || (!$hasBlock && $value && $this->MatchChar(';'))) {
+ $this->forget();
+ return $this->NewObj5('Less_Tree_Directive',array($name, $value, $rules, $index, $this->env->currentFileInfo));
+ }
+
+ $this->restore();
+ }
+
+
+ //
+ // A Value is a comma-delimited list of Expressions
+ //
+ // font-family: Baskerville, Georgia, serif;
+ //
+ // In a Rule, a Value represents everything after the `:`,
+ // and before the `;`.
+ //
+ private function parseValue(){
+ $expressions = array();
+
+ do{
+ $e = $this->parseExpression();
+ if( $e ){
+ $expressions[] = $e;
+ if (! $this->MatchChar(',')) {
+ break;
+ }
+ }
+ }while($e);
+
+ if( $expressions ){
+ return $this->NewObj1('Less_Tree_Value',$expressions);
+ }
+ }
+
+ private function parseImportant (){
+ if( $this->PeekChar('!') && $this->MatchReg('/\\G! *important/') ){
+ return ' !important';
+ }
+ }
+
+ private function parseSub (){
+
+ if( $this->MatchChar('(') ){
+ $a = $this->parseAddition();
+ if( $a ){
+ $this->expectChar(')');
+ return $this->NewObj2('Less_Tree_Expression',array( array($a), true) ); //instead of $e->parens = true so the value is cached
+ }
+ }
+ }
+
+
+ /**
+ * Parses multiplication operation
+ *
+ * @return Less_Tree_Operation|null
+ */
+ function parseMultiplication(){
+
+ $return = $m = $this->parseOperand();
+ if( $return ){
+ while( true ){
+
+ $isSpaced = $this->isWhitespace( -1 );
+
+ if( $this->PeekReg('/\\G\/[*\/]/') ){
+ break;
+ }
+
+ $op = $this->MatchChar('/');
+ if( !$op ){
+ $op = $this->MatchChar('*');
+ if( !$op ){
+ break;
+ }
+ }
+
+ $a = $this->parseOperand();
+
+ if(!$a) { break; }
+
+ $m->parensInOp = true;
+ $a->parensInOp = true;
+ $return = $this->NewObj3('Less_Tree_Operation',array( $op, array( $return, $a ), $isSpaced) );
+ }
+ }
+ return $return;
+
+ }
+
+
+ /**
+ * Parses an addition operation
+ *
+ * @return Less_Tree_Operation|null
+ */
+ private function parseAddition (){
+
+ $return = $m = $this->parseMultiplication();
+ if( $return ){
+ while( true ){
+
+ $isSpaced = $this->isWhitespace( -1 );
+
+ $op = $this->MatchReg('/\\G[-+]\s+/');
+ if( $op ){
+ $op = $op[0];
+ }else{
+ if( !$isSpaced ){
+ $op = $this->match(array('#+','#-'));
+ }
+ if( !$op ){
+ break;
+ }
+ }
+
+ $a = $this->parseMultiplication();
+ if( !$a ){
+ break;
+ }
+
+ $m->parensInOp = true;
+ $a->parensInOp = true;
+ $return = $this->NewObj3('Less_Tree_Operation',array($op, array($return, $a), $isSpaced));
+ }
+ }
+
+ return $return;
+ }
+
+
+ /**
+ * Parses the conditions
+ *
+ * @return Less_Tree_Condition|null
+ */
+ private function parseConditions() {
+ $index = $this->pos;
+ $return = $a = $this->parseCondition();
+ if( $a ){
+ while( true ){
+ if( !$this->PeekReg('/\\G,\s*(not\s*)?\(/') || !$this->MatchChar(',') ){
+ break;
+ }
+ $b = $this->parseCondition();
+ if( !$b ){
+ break;
+ }
+
+ $return = $this->NewObj4('Less_Tree_Condition',array('or', $return, $b, $index));
+ }
+ return $return;
+ }
+ }
+
+ private function parseCondition() {
+ $index = $this->pos;
+ $negate = false;
+ $c = null;
+
+ if ($this->MatchReg('/\\Gnot/')) $negate = true;
+ $this->expectChar('(');
+ $a = $this->MatchFuncs(array('parseAddition','parseEntitiesKeyword','parseEntitiesQuoted'));
+
+ if( $a ){
+ $op = $this->MatchReg('/\\G(?:>=|<=|=<|[<=>])/');
+ if( $op ){
+ $b = $this->MatchFuncs(array('parseAddition','parseEntitiesKeyword','parseEntitiesQuoted'));
+ if( $b ){
+ $c = $this->NewObj5('Less_Tree_Condition',array($op[0], $a, $b, $index, $negate));
+ } else {
+ $this->Error('Unexpected expression');
+ }
+ } else {
+ $k = $this->NewObj1('Less_Tree_Keyword','true');
+ $c = $this->NewObj5('Less_Tree_Condition',array('=', $a, $k, $index, $negate));
+ }
+ $this->expectChar(')');
+ return $this->MatchReg('/\\Gand/') ? $this->NewObj3('Less_Tree_Condition',array('and', $c, $this->parseCondition())) : $c;
+ }
+ }
+
+ /**
+ * An operand is anything that can be part of an operation,
+ * such as a Color, or a Variable
+ *
+ */
+ private function parseOperand (){
+
+ $negate = false;
+ $offset = $this->pos+1;
+ if( $offset >= $this->input_len ){
+ return;
+ }
+ $char = $this->input[$offset];
+ if( $char === '@' || $char === '(' ){
+ $negate = $this->MatchChar('-');
+ }
+
+ $o = $this->MatchFuncs(array('parseSub','parseEntitiesDimension','parseEntitiesColor','parseEntitiesVariable','parseEntitiesCall'));
+
+ if( $negate ){
+ $o->parensInOp = true;
+ $o = $this->NewObj1('Less_Tree_Negative',$o);
+ }
+
+ return $o;
+ }
+
+
+ /**
+ * Expressions either represent mathematical operations,
+ * or white-space delimited Entities.
+ *
+ * 1px solid black
+ * @var * 2
+ *
+ * @return Less_Tree_Expression|null
+ */
+ private function parseExpression (){
+ $entities = array();
+
+ do{
+ $e = $this->MatchFuncs(array('parseAddition','parseEntity'));
+ if( $e ){
+ $entities[] = $e;
+ // operations do not allow keyword "/" dimension (e.g. small/20px) so we support that here
+ if( !$this->PeekReg('/\\G\/[\/*]/') ){
+ $delim = $this->MatchChar('/');
+ if( $delim ){
+ $entities[] = $this->NewObj1('Less_Tree_Anonymous',$delim);
+ }
+ }
+ }
+ }while($e);
+
+ if( $entities ){
+ return $this->NewObj1('Less_Tree_Expression',$entities);
+ }
+ }
+
+
+ /**
+ * Parse a property
+ * eg: 'min-width', 'orientation', etc
+ *
+ * @return string
+ */
+ private function parseProperty (){
+ $name = $this->MatchReg('/\\G(\*?-?[_a-zA-Z0-9-]+)\s*:/');
+ if( $name ){
+ return $name[1];
+ }
+ }
+
+
+ /**
+ * Parse a rule property
+ * eg: 'color', 'width', 'height', etc
+ *
+ * @return string
+ */
+ private function parseRuleProperty(){
+ $offset = $this->pos;
+ $name = array();
+ $index = array();
+ $length = 0;
+
+
+ $this->rulePropertyMatch('/\\G(\*?)/', $offset, $length, $index, $name );
+ while( $this->rulePropertyMatch('/\\G((?:[\w-]+)|(?:@\{[\w-]+\}))/', $offset, $length, $index, $name )); // !
+
+ if( (count($name) > 1) && $this->rulePropertyMatch('/\\G\s*((?:\+_|\+)?)\s*:/', $offset, $length, $index, $name) ){
+ // at last, we have the complete match now. move forward,
+ // convert name particles to tree objects and return:
+ $this->skipWhitespace($length);
+
+ if( $name[0] === '' ){
+ array_shift($name);
+ array_shift($index);
+ }
+ foreach($name as $k => $s ){
+ if( !$s || $s[0] !== '@' ){
+ $name[$k] = $this->NewObj1('Less_Tree_Keyword',$s);
+ }else{
+ $name[$k] = $this->NewObj3('Less_Tree_Variable',array('@' . substr($s,2,-1), $index[$k], $this->env->currentFileInfo));
+ }
+ }
+ return $name;
+ }
+
+
+ }
+
+ private function rulePropertyMatch( $re, &$offset, &$length, &$index, &$name ){
+ preg_match($re, $this->input, $a, 0, $offset);
+ if( $a ){
+ $index[] = $this->pos + $length;
+ $length += strlen($a[0]);
+ $offset += strlen($a[0]);
+ $name[] = $a[1];
+ return true;
+ }
+ }
+
+ public static function serializeVars( $vars ){
+ $s = '';
+
+ foreach($vars as $name => $value){
+ $s .= (($name[0] === '@') ? '' : '@') . $name .': '. $value . ((substr($value,-1) === ';') ? '' : ';');
+ }
+
+ return $s;
+ }
+
+
+ /**
+ * Some versions of php have trouble with method_exists($a,$b) if $a is not an object
+ *
+ * @param string $b
+ */
+ public static function is_method($a,$b){
+ return is_object($a) && method_exists($a,$b);
+ }
+
+
+ /**
+ * Round numbers similarly to javascript
+ * eg: 1.499999 to 1 instead of 2
+ *
+ */
+ public static function round($i, $precision = 0){
+
+ $precision = pow(10,$precision);
+ $i = $i*$precision;
+
+ $ceil = ceil($i);
+ $floor = floor($i);
+ if( ($ceil - $i) <= ($i - $floor) ){
+ return $ceil/$precision;
+ }else{
+ return $floor/$precision;
+ }
+ }
+
+
+ /**
+ * Create Less_Tree_* objects and optionally generate a cache string
+ *
+ * @return mixed
+ */
+ public function NewObj0($class){
+ $obj = new $class();
+ if( $this->CacheEnabled() ){
+ $obj->cache_string = ' new '.$class.'()';
+ }
+ return $obj;
+ }
+
+ public function NewObj1($class, $arg){
+ $obj = new $class( $arg );
+ if( $this->CacheEnabled() ){
+ $obj->cache_string = ' new '.$class.'('.Less_Parser::ArgString($arg).')';
+ }
+ return $obj;
+ }
+
+ public function NewObj2($class, $args){
+ $obj = new $class( $args[0], $args[1] );
+ if( $this->CacheEnabled() ){
+ $this->ObjCache( $obj, $class, $args);
+ }
+ return $obj;
+ }
+
+ public function NewObj3($class, $args){
+ $obj = new $class( $args[0], $args[1], $args[2] );
+ if( $this->CacheEnabled() ){
+ $this->ObjCache( $obj, $class, $args);
+ }
+ return $obj;
+ }
+
+ public function NewObj4($class, $args){
+ $obj = new $class( $args[0], $args[1], $args[2], $args[3] );
+ if( $this->CacheEnabled() ){
+ $this->ObjCache( $obj, $class, $args);
+ }
+ return $obj;
+ }
+
+ public function NewObj5($class, $args){
+ $obj = new $class( $args[0], $args[1], $args[2], $args[3], $args[4] );
+ if( $this->CacheEnabled() ){
+ $this->ObjCache( $obj, $class, $args);
+ }
+ return $obj;
+ }
+
+ public function NewObj6($class, $args){
+ $obj = new $class( $args[0], $args[1], $args[2], $args[3], $args[4], $args[5] );
+ if( $this->CacheEnabled() ){
+ $this->ObjCache( $obj, $class, $args);
+ }
+ return $obj;
+ }
+
+ public function NewObj7($class, $args){
+ $obj = new $class( $args[0], $args[1], $args[2], $args[3], $args[4], $args[5], $args[6] );
+ if( $this->CacheEnabled() ){
+ $this->ObjCache( $obj, $class, $args);
+ }
+ return $obj;
+ }
+
+ //caching
+ public function ObjCache($obj, $class, $args=array()){
+ $obj->cache_string = ' new '.$class.'('. self::ArgCache($args).')';
+ }
+
+ public function ArgCache($args){
+ return implode(',',array_map( array('Less_Parser','ArgString'),$args));
+ }
+
+
+ /**
+ * Convert an argument to a string for use in the parser cache
+ *
+ * @return string
+ */
+ public static function ArgString($arg){
+
+ $type = gettype($arg);
+
+ if( $type === 'object'){
+ $string = $arg->cache_string;
+ unset($arg->cache_string);
+ return $string;
+
+ }elseif( $type === 'array' ){
+ $string = ' Array(';
+ foreach($arg as $k => $a){
+ $string .= var_export($k,true).' => '.self::ArgString($a).',';
+ }
+ return $string . ')';
+ }
+
+ return var_export($arg,true);
+ }
+
+ public function Error($msg){
+ throw new Less_Exception_Parser($msg, null, $this->furthest, $this->env->currentFileInfo);
+ }
+
+ public static function WinPath($path){
+ return str_replace('\\', '/', $path);
+ }
+
+ public static function AbsPath($path, $winPath = false){
+ if (strpos($path, '//') !== false && preg_match('_^(https?:)?//\\w+(\\.\\w+)+/\\w+_i', $path)) {
+ return $winPath ? '' : false;
+ } else {
+ $path = realpath($path);
+ if ($winPath) {
+ $path = self::WinPath($path);
+ }
+ return $path;
+ }
+ }
+
+ public function CacheEnabled(){
+ return (Less_Parser::$options['cache_method'] && (Less_Cache::$cache_dir || (Less_Parser::$options['cache_method'] == 'callback')));
+ }
+
+}
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/SourceMap/Base64VLQ.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/SourceMap/Base64VLQ.php
new file mode 100644
index 000000000..f5b200c35
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/SourceMap/Base64VLQ.php
@@ -0,0 +1,187 @@
+ 0, 'B' => 1, 'C' => 2, 'D' => 3, 'E' => 4, 'F' => 5, 'G' => 6,
+ 'H' => 7,'I' => 8, 'J' => 9, 'K' => 10, 'L' => 11, 'M' => 12, 'N' => 13,
+ 'O' => 14, 'P' => 15, 'Q' => 16, 'R' => 17, 'S' => 18, 'T' => 19, 'U' => 20,
+ 'V' => 21, 'W' => 22, 'X' => 23, 'Y' => 24, 'Z' => 25, 'a' => 26, 'b' => 27,
+ 'c' => 28, 'd' => 29, 'e' => 30, 'f' => 31, 'g' => 32, 'h' => 33, 'i' => 34,
+ 'j' => 35, 'k' => 36, 'l' => 37, 'm' => 38, 'n' => 39, 'o' => 40, 'p' => 41,
+ 'q' => 42, 'r' => 43, 's' => 44, 't' => 45, 'u' => 46, 'v' => 47, 'w' => 48,
+ 'x' => 49, 'y' => 50, 'z' => 51, 0 => 52, 1 => 53, 2 => 54, 3 => 55, 4 => 56,
+ 5 => 57, 6 => 58, 7 => 59, 8 => 60, 9 => 61, '+' => 62, '/' => 63,
+ );
+
+ /**
+ * Integer to char map
+ *
+ * @var array
+ */
+ private $intToCharMap = array(
+ 0 => 'A', 1 => 'B', 2 => 'C', 3 => 'D', 4 => 'E', 5 => 'F', 6 => 'G',
+ 7 => 'H', 8 => 'I', 9 => 'J', 10 => 'K', 11 => 'L', 12 => 'M', 13 => 'N',
+ 14 => 'O', 15 => 'P', 16 => 'Q', 17 => 'R', 18 => 'S', 19 => 'T', 20 => 'U',
+ 21 => 'V', 22 => 'W', 23 => 'X', 24 => 'Y', 25 => 'Z', 26 => 'a', 27 => 'b',
+ 28 => 'c', 29 => 'd', 30 => 'e', 31 => 'f', 32 => 'g', 33 => 'h', 34 => 'i',
+ 35 => 'j', 36 => 'k', 37 => 'l', 38 => 'm', 39 => 'n', 40 => 'o', 41 => 'p',
+ 42 => 'q', 43 => 'r', 44 => 's', 45 => 't', 46 => 'u', 47 => 'v', 48 => 'w',
+ 49 => 'x', 50 => 'y', 51 => 'z', 52 => '0', 53 => '1', 54 => '2', 55 => '3',
+ 56 => '4', 57 => '5', 58 => '6', 59 => '7', 60 => '8', 61 => '9', 62 => '+',
+ 63 => '/',
+ );
+
+ /**
+ * Constructor
+ */
+ public function __construct(){
+ // I leave it here for future reference
+ // foreach(str_split('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/') as $i => $char)
+ // {
+ // $this->charToIntMap[$char] = $i;
+ // $this->intToCharMap[$i] = $char;
+ // }
+ }
+
+ /**
+ * Convert from a two-complement value to a value where the sign bit is
+ * is placed in the least significant bit. For example, as decimals:
+ * 1 becomes 2 (10 binary), -1 becomes 3 (11 binary)
+ * 2 becomes 4 (100 binary), -2 becomes 5 (101 binary)
+ * We generate the value for 32 bit machines, hence -2147483648 becomes 1, not 4294967297,
+ * even on a 64 bit machine.
+ * @param string $aValue
+ */
+ public function toVLQSigned($aValue){
+ return 0xffffffff & ($aValue < 0 ? ((-$aValue) << 1) + 1 : ($aValue << 1) + 0);
+ }
+
+ /**
+ * Convert to a two-complement value from a value where the sign bit is
+ * is placed in the least significant bit. For example, as decimals:
+ * 2 (10 binary) becomes 1, 3 (11 binary) becomes -1
+ * 4 (100 binary) becomes 2, 5 (101 binary) becomes -2
+ * We assume that the value was generated with a 32 bit machine in mind.
+ * Hence
+ * 1 becomes -2147483648
+ * even on a 64 bit machine.
+ * @param integer $aValue
+ */
+ public function fromVLQSigned($aValue){
+ return $aValue & 1 ? $this->zeroFill(~$aValue + 2, 1) | (-1 - 0x7fffffff) : $this->zeroFill($aValue, 1);
+ }
+
+ /**
+ * Return the base 64 VLQ encoded value.
+ *
+ * @param string $aValue The value to encode
+ * @return string The encoded value
+ */
+ public function encode($aValue){
+ $encoded = '';
+ $vlq = $this->toVLQSigned($aValue);
+ do
+ {
+ $digit = $vlq & $this->mask;
+ $vlq = $this->zeroFill($vlq, $this->shift);
+ if($vlq > 0){
+ $digit |= $this->continuationBit;
+ }
+ $encoded .= $this->base64Encode($digit);
+ } while($vlq > 0);
+
+ return $encoded;
+ }
+
+ /**
+ * Return the value decoded from base 64 VLQ.
+ *
+ * @param string $encoded The encoded value to decode
+ * @return integer The decoded value
+ */
+ public function decode($encoded){
+ $vlq = 0;
+ $i = 0;
+ do
+ {
+ $digit = $this->base64Decode($encoded[$i]);
+ $vlq |= ($digit & $this->mask) << ($i * $this->shift);
+ $i++;
+ } while($digit & $this->continuationBit);
+
+ return $this->fromVLQSigned($vlq);
+ }
+
+ /**
+ * Right shift with zero fill.
+ *
+ * @param integer $a number to shift
+ * @param integer $b number of bits to shift
+ * @return integer
+ */
+ public function zeroFill($a, $b){
+ return ($a >= 0) ? ($a >> $b) : ($a >> $b) & (PHP_INT_MAX >> ($b - 1));
+ }
+
+ /**
+ * Encode single 6-bit digit as base64.
+ *
+ * @param integer $number
+ * @return string
+ * @throws Exception If the number is invalid
+ */
+ public function base64Encode($number){
+ if($number < 0 || $number > 63){
+ throw new Exception(sprintf('Invalid number "%s" given. Must be between 0 and 63.', $number));
+ }
+ return $this->intToCharMap[$number];
+ }
+
+ /**
+ * Decode single 6-bit digit from base64
+ *
+ * @param string $char
+ * @return number
+ * @throws Exception If the number is invalid
+ */
+ public function base64Decode($char){
+ if(!array_key_exists($char, $this->charToIntMap)){
+ throw new Exception(sprintf('Invalid base 64 digit "%s" given.', $char));
+ }
+ return $this->charToIntMap[$char];
+ }
+
+}
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/SourceMap/Generator.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/SourceMap/Generator.php
new file mode 100644
index 000000000..9509aea71
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/SourceMap/Generator.php
@@ -0,0 +1,365 @@
+ '',
+
+ // an optional name of the generated code that this source map is associated with.
+ 'sourceMapFilename' => null,
+
+ // url of the map
+ 'sourceMapURL' => null,
+
+ // absolute path to a file to write the map to
+ 'sourceMapWriteTo' => null,
+
+ // output source contents?
+ 'outputSourceFiles' => false,
+
+ // base path for filename normalization
+ 'sourceMapRootpath' => '',
+
+ // base path for filename normalization
+ 'sourceMapBasepath' => ''
+ );
+
+ /**
+ * The base64 VLQ encoder
+ *
+ * @var Less_SourceMap_Base64VLQ
+ */
+ protected $encoder;
+
+ /**
+ * Array of mappings
+ *
+ * @var array
+ */
+ protected $mappings = array();
+
+ /**
+ * The root node
+ *
+ * @var Less_Tree_Ruleset
+ */
+ protected $root;
+
+ /**
+ * Array of contents map
+ *
+ * @var array
+ */
+ protected $contentsMap = array();
+
+ /**
+ * File to content map
+ *
+ * @var array
+ */
+ protected $sources = array();
+ protected $source_keys = array();
+
+ /**
+ * Constructor
+ *
+ * @param Less_Tree_Ruleset $root The root node
+ * @param array $options Array of options
+ */
+ public function __construct(Less_Tree_Ruleset $root, $contentsMap, $options = array()){
+ $this->root = $root;
+ $this->contentsMap = $contentsMap;
+ $this->encoder = new Less_SourceMap_Base64VLQ();
+
+ $this->SetOptions($options);
+
+ $this->options['sourceMapRootpath'] = $this->fixWindowsPath($this->options['sourceMapRootpath'], true);
+ $this->options['sourceMapBasepath'] = $this->fixWindowsPath($this->options['sourceMapBasepath'], true);
+ }
+
+ /**
+ * Generates the CSS
+ *
+ * @return string
+ */
+ public function generateCSS(){
+ $output = new Less_Output_Mapped($this->contentsMap, $this);
+
+ // catch the output
+ $this->root->genCSS($output);
+
+
+ $sourceMapUrl = $this->getOption('sourceMapURL');
+ $sourceMapFilename = $this->getOption('sourceMapFilename');
+ $sourceMapContent = $this->generateJson();
+ $sourceMapWriteTo = $this->getOption('sourceMapWriteTo');
+
+ if( !$sourceMapUrl && $sourceMapFilename ){
+ $sourceMapUrl = $this->normalizeFilename($sourceMapFilename);
+ }
+
+ // write map to a file
+ if( $sourceMapWriteTo ){
+ $this->saveMap($sourceMapWriteTo, $sourceMapContent);
+ }
+
+ // inline the map
+ if( !$sourceMapUrl ){
+ $sourceMapUrl = sprintf('data:application/json,%s', Less_Functions::encodeURIComponent($sourceMapContent));
+ }
+
+ if( $sourceMapUrl ){
+ $output->add( sprintf('/*# sourceMappingURL=%s */', $sourceMapUrl) );
+ }
+
+ return $output->toString();
+ }
+
+ /**
+ * Saves the source map to a file
+ *
+ * @param string $file The absolute path to a file
+ * @param string $content The content to write
+ * @throws Exception If the file could not be saved
+ */
+ protected function saveMap($file, $content){
+ $dir = dirname($file);
+ // directory does not exist
+ if( !is_dir($dir) ){
+ // FIXME: create the dir automatically?
+ throw new Exception(sprintf('The directory "%s" does not exist. Cannot save the source map.', $dir));
+ }
+ // FIXME: proper saving, with dir write check!
+ if(file_put_contents($file, $content) === false){
+ throw new Exception(sprintf('Cannot save the source map to "%s"', $file));
+ }
+ return true;
+ }
+
+ /**
+ * Normalizes the filename
+ *
+ * @param string $filename
+ * @return string
+ */
+ protected function normalizeFilename($filename){
+
+ $filename = $this->fixWindowsPath($filename);
+
+ $rootpath = $this->getOption('sourceMapRootpath');
+ $basePath = $this->getOption('sourceMapBasepath');
+
+ // "Trim" the 'sourceMapBasepath' from the output filename.
+ if (is_string($basePath) && strpos($filename, $basePath) === 0) {
+ $filename = substr($filename, strlen($basePath));
+ }
+
+ // Remove extra leading path separators.
+ if(strpos($filename, '\\') === 0 || strpos($filename, '/') === 0){
+ $filename = substr($filename, 1);
+ }
+
+ return $rootpath . $filename;
+ }
+
+ /**
+ * Adds a mapping
+ *
+ * @param integer $generatedLine The line number in generated file
+ * @param integer $generatedColumn The column number in generated file
+ * @param integer $originalLine The line number in original file
+ * @param integer $originalColumn The column number in original file
+ * @param string $sourceFile The original source file
+ */
+ public function addMapping($generatedLine, $generatedColumn, $originalLine, $originalColumn, $fileInfo ){
+
+ $this->mappings[] = array(
+ 'generated_line' => $generatedLine,
+ 'generated_column' => $generatedColumn,
+ 'original_line' => $originalLine,
+ 'original_column' => $originalColumn,
+ 'source_file' => $fileInfo['currentUri']
+ );
+
+ $this->sources[$fileInfo['currentUri']] = $fileInfo['filename'];
+ }
+
+
+ /**
+ * Generates the JSON source map
+ *
+ * @return string
+ * @see https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#
+ */
+ protected function generateJson(){
+
+ $sourceMap = array();
+ $mappings = $this->generateMappings();
+
+ // File version (always the first entry in the object) and must be a positive integer.
+ $sourceMap['version'] = self::VERSION;
+
+
+ // An optional name of the generated code that this source map is associated with.
+ $file = $this->getOption('sourceMapFilename');
+ if( $file ){
+ $sourceMap['file'] = $file;
+ }
+
+
+ // An optional source root, useful for relocating source files on a server or removing repeated values in the 'sources' entry. This value is prepended to the individual entries in the 'source' field.
+ $root = $this->getOption('sourceRoot');
+ if( $root ){
+ $sourceMap['sourceRoot'] = $root;
+ }
+
+
+ // A list of original sources used by the 'mappings' entry.
+ $sourceMap['sources'] = array();
+ foreach($this->sources as $source_uri => $source_filename){
+ $sourceMap['sources'][] = $this->normalizeFilename($source_filename);
+ }
+
+
+ // A list of symbol names used by the 'mappings' entry.
+ $sourceMap['names'] = array();
+
+ // A string with the encoded mapping data.
+ $sourceMap['mappings'] = $mappings;
+
+ if( $this->getOption('outputSourceFiles') ){
+ // An optional list of source content, useful when the 'source' can't be hosted.
+ // The contents are listed in the same order as the sources above.
+ // 'null' may be used if some original sources should be retrieved by name.
+ $sourceMap['sourcesContent'] = $this->getSourcesContent();
+ }
+
+ // less.js compat fixes
+ if( count($sourceMap['sources']) && empty($sourceMap['sourceRoot']) ){
+ unset($sourceMap['sourceRoot']);
+ }
+
+ return json_encode($sourceMap);
+ }
+
+ /**
+ * Returns the sources contents
+ *
+ * @return array|null
+ */
+ protected function getSourcesContent(){
+ if(empty($this->sources)){
+ return;
+ }
+ $content = array();
+ foreach($this->sources as $sourceFile){
+ $content[] = file_get_contents($sourceFile);
+ }
+ return $content;
+ }
+
+ /**
+ * Generates the mappings string
+ *
+ * @return string
+ */
+ public function generateMappings(){
+
+ if( !count($this->mappings) ){
+ return '';
+ }
+
+ $this->source_keys = array_flip(array_keys($this->sources));
+
+
+ // group mappings by generated line number.
+ $groupedMap = $groupedMapEncoded = array();
+ foreach($this->mappings as $m){
+ $groupedMap[$m['generated_line']][] = $m;
+ }
+ ksort($groupedMap);
+
+ $lastGeneratedLine = $lastOriginalIndex = $lastOriginalLine = $lastOriginalColumn = 0;
+
+ foreach($groupedMap as $lineNumber => $line_map){
+ while(++$lastGeneratedLine < $lineNumber){
+ $groupedMapEncoded[] = ';';
+ }
+
+ $lineMapEncoded = array();
+ $lastGeneratedColumn = 0;
+
+ foreach($line_map as $m){
+ $mapEncoded = $this->encoder->encode($m['generated_column'] - $lastGeneratedColumn);
+ $lastGeneratedColumn = $m['generated_column'];
+
+ // find the index
+ if( $m['source_file'] ){
+ $index = $this->findFileIndex($m['source_file']);
+ if( $index !== false ){
+ $mapEncoded .= $this->encoder->encode($index - $lastOriginalIndex);
+ $lastOriginalIndex = $index;
+
+ // lines are stored 0-based in SourceMap spec version 3
+ $mapEncoded .= $this->encoder->encode($m['original_line'] - 1 - $lastOriginalLine);
+ $lastOriginalLine = $m['original_line'] - 1;
+
+ $mapEncoded .= $this->encoder->encode($m['original_column'] - $lastOriginalColumn);
+ $lastOriginalColumn = $m['original_column'];
+ }
+ }
+
+ $lineMapEncoded[] = $mapEncoded;
+ }
+
+ $groupedMapEncoded[] = implode(',', $lineMapEncoded) . ';';
+ }
+
+ return rtrim(implode($groupedMapEncoded), ';');
+ }
+
+ /**
+ * Finds the index for the filename
+ *
+ * @param string $filename
+ * @return integer|false
+ */
+ protected function findFileIndex($filename){
+ return $this->source_keys[$filename];
+ }
+
+ /**
+ * fix windows paths
+ * @param string $path
+ * @return string
+ */
+ public function fixWindowsPath($path, $addEndSlash = false){
+ $slash = ($addEndSlash) ? '/' : '';
+ if( !empty($path) ){
+ $path = str_replace('\\', '/', $path);
+ $path = rtrim($path,'/') . $slash;
+ }
+
+ return $path;
+ }
+
+}
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree.php
new file mode 100644
index 000000000..6fb104bc7
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree.php
@@ -0,0 +1,90 @@
+genCSS($output);
+ return $output->toString();
+ }
+
+
+ /**
+ * Generate CSS by adding it to the output object
+ *
+ * @param Less_Output $output The output
+ * @return void
+ */
+ public function genCSS($output){}
+
+
+ /**
+ * @param Less_Tree_Ruleset[] $rules
+ */
+ public static function outputRuleset( $output, $rules ){
+
+ $ruleCnt = count($rules);
+ Less_Environment::$tabLevel++;
+
+
+ // Compressed
+ if( Less_Parser::$options['compress'] ){
+ $output->add('{');
+ for( $i = 0; $i < $ruleCnt; $i++ ){
+ $rules[$i]->genCSS( $output );
+ }
+
+ $output->add( '}' );
+ Less_Environment::$tabLevel--;
+ return;
+ }
+
+
+ // Non-compressed
+ $tabSetStr = "\n".str_repeat( Less_Parser::$options['indentation'] , Less_Environment::$tabLevel-1 );
+ $tabRuleStr = $tabSetStr.Less_Parser::$options['indentation'];
+
+ $output->add( " {" );
+ for($i = 0; $i < $ruleCnt; $i++ ){
+ $output->add( $tabRuleStr );
+ $rules[$i]->genCSS( $output );
+ }
+ Less_Environment::$tabLevel--;
+ $output->add( $tabSetStr.'}' );
+
+ }
+
+ public function accept($visitor){}
+
+
+ public static function ReferencedArray($rules){
+ foreach($rules as $rule){
+ if( method_exists($rule, 'markReferenced') ){
+ $rule->markReferenced();
+ }
+ }
+ }
+
+
+ /**
+ * Requires php 5.3+
+ */
+ public static function __set_state($args){
+
+ $class = get_called_class();
+ $obj = new $class(null,null,null,null);
+ foreach($args as $key => $val){
+ $obj->$key = $val;
+ }
+ return $obj;
+ }
+
+}
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Alpha.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Alpha.php
new file mode 100644
index 000000000..935377da3
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Alpha.php
@@ -0,0 +1,51 @@
+value = $val;
+ }
+
+ //function accept( $visitor ){
+ // $this->value = $visitor->visit( $this->value );
+ //}
+
+ public function compile($env){
+
+ if( is_object($this->value) ){
+ $this->value = $this->value->compile($env);
+ }
+
+ return $this;
+ }
+
+ /**
+ * @see Less_Tree::genCSS
+ */
+ public function genCSS( $output ){
+
+ $output->add( "alpha(opacity=" );
+
+ if( is_string($this->value) ){
+ $output->add( $this->value );
+ }else{
+ $this->value->genCSS( $output);
+ }
+
+ $output->add( ')' );
+ }
+
+ public function toCSS(){
+ return "alpha(opacity=" . (is_string($this->value) ? $this->value : $this->value->toCSS()) . ")";
+ }
+
+
+}
\ No newline at end of file
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Anonymous.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Anonymous.php
new file mode 100644
index 000000000..8889dc0f3
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Anonymous.php
@@ -0,0 +1,58 @@
+value = $value;
+ $this->index = $index;
+ $this->mapLines = $mapLines;
+ $this->currentFileInfo = $currentFileInfo;
+ }
+
+ public function compile(){
+ return new Less_Tree_Anonymous($this->value, $this->index, $this->currentFileInfo, $this->mapLines);
+ }
+
+ public function compare($x){
+ if( !is_object($x) ){
+ return -1;
+ }
+
+ $left = $this->toCSS();
+ $right = $x->toCSS();
+
+ if( $left === $right ){
+ return 0;
+ }
+
+ return $left < $right ? -1 : 1;
+ }
+
+ /**
+ * @see Less_Tree::genCSS
+ */
+ public function genCSS( $output ){
+ $output->add( $this->value, $this->currentFileInfo, $this->index, $this->mapLines );
+ }
+
+ public function toCSS(){
+ return $this->value;
+ }
+
+}
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Assignment.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Assignment.php
new file mode 100644
index 000000000..238000647
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Assignment.php
@@ -0,0 +1,39 @@
+key = $key;
+ $this->value = $val;
+ }
+
+ public function accept( $visitor ){
+ $this->value = $visitor->visitObj( $this->value );
+ }
+
+ public function compile($env) {
+ return new Less_Tree_Assignment( $this->key, $this->value->compile($env));
+ }
+
+ /**
+ * @see Less_Tree::genCSS
+ */
+ public function genCSS( $output ){
+ $output->add( $this->key . '=' );
+ $this->value->genCSS( $output );
+ }
+
+ public function toCss(){
+ return $this->key . '=' . $this->value->toCSS();
+ }
+}
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Attribute.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Attribute.php
new file mode 100644
index 000000000..32b8900d8
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Attribute.php
@@ -0,0 +1,54 @@
+key = $key;
+ $this->op = $op;
+ $this->value = $value;
+ }
+
+ public function compile($env){
+
+ $key_obj = is_object($this->key);
+ $val_obj = is_object($this->value);
+
+ if( !$key_obj && !$val_obj ){
+ return $this;
+ }
+
+ return new Less_Tree_Attribute(
+ $key_obj ? $this->key->compile($env) : $this->key ,
+ $this->op,
+ $val_obj ? $this->value->compile($env) : $this->value);
+ }
+
+ /**
+ * @see Less_Tree::genCSS
+ */
+ public function genCSS( $output ){
+ $output->add( $this->toCSS() );
+ }
+
+ public function toCSS(){
+ $value = $this->key;
+
+ if( $this->op ){
+ $value .= $this->op;
+ $value .= (is_object($this->value) ? $this->value->toCSS() : $this->value);
+ }
+
+ return '[' . $value . ']';
+ }
+}
\ No newline at end of file
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Call.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Call.php
new file mode 100644
index 000000000..3c3382d10
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Call.php
@@ -0,0 +1,121 @@
+name = $name;
+ $this->args = $args;
+ $this->index = $index;
+ $this->currentFileInfo = $currentFileInfo;
+ }
+
+ public function accept( $visitor ){
+ $this->args = $visitor->visitArray( $this->args );
+ }
+
+ //
+ // 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=null){
+ $args = array();
+ foreach($this->args as $a){
+ $args[] = $a->compile($env);
+ }
+
+ $nameLC = strtolower($this->name);
+ switch($nameLC){
+ case '%':
+ $nameLC = '_percent';
+ break;
+
+ case 'get-unit':
+ $nameLC = 'getunit';
+ break;
+
+ case 'data-uri':
+ $nameLC = 'datauri';
+ break;
+
+ case 'svg-gradient':
+ $nameLC = 'svggradient';
+ break;
+ }
+
+ $result = null;
+ if( $nameLC === 'default' ){
+ $result = Less_Tree_DefaultFunc::compile();
+
+ }else{
+
+ if( method_exists('Less_Functions',$nameLC) ){ // 1.
+ try {
+
+ $func = new Less_Functions($env, $this->currentFileInfo);
+ $result = call_user_func_array( array($func,$nameLC),$args);
+
+ } catch (Exception $e) {
+ throw new Less_Exception_Compiler('error evaluating function `' . $this->name . '` '.$e->getMessage().' index: '. $this->index);
+ }
+ } elseif( isset( $env->functions[$nameLC] ) && is_callable( $env->functions[$nameLC] ) ) {
+ try {
+ $result = call_user_func_array( $env->functions[$nameLC], $args );
+ } catch (Exception $e) {
+ throw new Less_Exception_Compiler('error evaluating function `' . $this->name . '` '.$e->getMessage().' index: '. $this->index);
+ }
+ }
+ }
+
+ if( $result !== null ){
+ return $result;
+ }
+
+
+ return new Less_Tree_Call( $this->name, $args, $this->index, $this->currentFileInfo );
+ }
+
+ /**
+ * @see Less_Tree::genCSS
+ */
+ public function genCSS( $output ){
+
+ $output->add( $this->name . '(', $this->currentFileInfo, $this->index );
+ $args_len = count($this->args);
+ for($i = 0; $i < $args_len; $i++ ){
+ $this->args[$i]->genCSS( $output );
+ if( $i + 1 < $args_len ){
+ $output->add( ', ' );
+ }
+ }
+
+ $output->add( ')' );
+ }
+
+
+ //public function toCSS(){
+ // return $this->compile()->toCSS();
+ //}
+
+}
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Color.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Color.php
new file mode 100644
index 000000000..77af07a6e
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Color.php
@@ -0,0 +1,230 @@
+rgb = $rgb;
+ $this->alpha = $a;
+ $this->isTransparentKeyword = true;
+ return;
+ }
+
+ $this->rgb = array();
+ if( is_array($rgb) ){
+ $this->rgb = $rgb;
+ }else if( strlen($rgb) == 6 ){
+ foreach(str_split($rgb, 2) as $c){
+ $this->rgb[] = hexdec($c);
+ }
+ }else{
+ foreach(str_split($rgb, 1) as $c){
+ $this->rgb[] = hexdec($c.$c);
+ }
+ }
+ $this->alpha = is_numeric($a) ? $a : 1;
+ }
+
+ public function compile(){
+ return $this;
+ }
+
+ public function luma(){
+ $r = $this->rgb[0] / 255;
+ $g = $this->rgb[1] / 255;
+ $b = $this->rgb[2] / 255;
+
+ $r = ($r <= 0.03928) ? $r / 12.92 : pow((($r + 0.055) / 1.055), 2.4);
+ $g = ($g <= 0.03928) ? $g / 12.92 : pow((($g + 0.055) / 1.055), 2.4);
+ $b = ($b <= 0.03928) ? $b / 12.92 : pow((($b + 0.055) / 1.055), 2.4);
+
+ return 0.2126 * $r + 0.7152 * $g + 0.0722 * $b;
+ }
+
+ /**
+ * @see Less_Tree::genCSS
+ */
+ public function genCSS( $output ){
+ $output->add( $this->toCSS() );
+ }
+
+ public function toCSS( $doNotCompress = false ){
+ $compress = Less_Parser::$options['compress'] && !$doNotCompress;
+ $alpha = Less_Functions::fround( $this->alpha );
+
+
+ //
+ // 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.
+ //
+ if( $alpha < 1 ){
+ if( ( $alpha === 0 || $alpha === 0.0 ) && isset($this->isTransparentKeyword) && $this->isTransparentKeyword ){
+ return 'transparent';
+ }
+
+ $values = array();
+ foreach($this->rgb as $c){
+ $values[] = Less_Functions::clamp( round($c), 255);
+ }
+ $values[] = $alpha;
+
+ $glue = ($compress ? ',' : ', ');
+ return "rgba(" . implode($glue, $values) . ")";
+ }else{
+
+ $color = $this->toRGB();
+
+ if( $compress ){
+
+ // Convert color to short format
+ if( $color[1] === $color[2] && $color[3] === $color[4] && $color[5] === $color[6]) {
+ $color = '#'.$color[1] . $color[3] . $color[5];
+ }
+ }
+
+ return $color;
+ }
+ }
+
+ //
+ // 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.
+ //
+
+ /**
+ * @param string $op
+ */
+ public function operate( $op, $other) {
+ $rgb = array();
+ $alpha = $this->alpha * (1 - $other->alpha) + $other->alpha;
+ for ($c = 0; $c < 3; $c++) {
+ $rgb[$c] = Less_Functions::operate( $op, $this->rgb[$c], $other->rgb[$c]);
+ }
+ return new Less_Tree_Color($rgb, $alpha);
+ }
+
+ public function toRGB(){
+ return $this->toHex($this->rgb);
+ }
+
+ 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;
+
+ $h = $s = 0;
+ if( $max !== $min ){
+ $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 );
+ }
+
+ //Adapted from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
+ public function toHSV() {
+ $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);
+
+ $v = $max;
+
+ $d = $max - $min;
+ if ($max === 0) {
+ $s = 0;
+ } else {
+ $s = $d / $max;
+ }
+
+ $h = 0;
+ if( $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, 'v'=> $v, 'a' => $a );
+ }
+
+ public function toARGB(){
+ $argb = array_merge( (array) Less_Parser::round($this->alpha * 255), $this->rgb);
+ return $this->toHex( $argb );
+ }
+
+ public function compare($x){
+
+ if( !property_exists( $x, 'rgb' ) ){
+ return -1;
+ }
+
+
+ return ($x->rgb[0] === $this->rgb[0] &&
+ $x->rgb[1] === $this->rgb[1] &&
+ $x->rgb[2] === $this->rgb[2] &&
+ $x->alpha === $this->alpha) ? 0 : -1;
+ }
+
+ public function toHex( $v ){
+
+ $ret = '#';
+ foreach($v as $c){
+ $c = Less_Functions::clamp( Less_Parser::round($c), 255);
+ if( $c < 16 ){
+ $ret .= '0';
+ }
+ $ret .= dechex($c);
+ }
+
+ return $ret;
+ }
+
+
+ /**
+ * @param string $keyword
+ */
+ public static function fromKeyword( $keyword ){
+ $keyword = strtolower($keyword);
+
+ if( Less_Colors::hasOwnProperty($keyword) ){
+ // detect named color
+ return new Less_Tree_Color(substr(Less_Colors::color($keyword), 1));
+ }
+
+ if( $keyword === 'transparent' ){
+ return new Less_Tree_Color( array(0, 0, 0), 0, true);
+ }
+ }
+
+}
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Comment.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Comment.php
new file mode 100644
index 000000000..7261284a2
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Comment.php
@@ -0,0 +1,51 @@
+value = $value;
+ $this->silent = !! $silent;
+ $this->currentFileInfo = $currentFileInfo;
+ }
+
+ /**
+ * @see Less_Tree::genCSS
+ */
+ public function genCSS( $output ){
+ //if( $this->debugInfo ){
+ //$output->add( tree.debugInfo($env, $this), $this->currentFileInfo, $this->index);
+ //}
+ $output->add( trim($this->value) );//TODO shouldn't need to trim, we shouldn't grab the \n
+ }
+
+ public function toCSS(){
+ return Less_Parser::$options['compress'] ? '' : $this->value;
+ }
+
+ public function isSilent(){
+ $isReference = ($this->currentFileInfo && isset($this->currentFileInfo['reference']) && (!isset($this->isReferenced) || !$this->isReferenced) );
+ $isCompressed = Less_Parser::$options['compress'] && !preg_match('/^\/\*!/', $this->value);
+ return $this->silent || $isReference || $isCompressed;
+ }
+
+ public function compile(){
+ return $this;
+ }
+
+ public function markReferenced(){
+ $this->isReferenced = true;
+ }
+
+}
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Condition.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Condition.php
new file mode 100644
index 000000000..929d33be9
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Condition.php
@@ -0,0 +1,72 @@
+op = trim($op);
+ $this->lvalue = $l;
+ $this->rvalue = $r;
+ $this->index = $i;
+ $this->negate = $negate;
+ }
+
+ public function accept($visitor){
+ $this->lvalue = $visitor->visitObj( $this->lvalue );
+ $this->rvalue = $visitor->visitObj( $this->rvalue );
+ }
+
+ public function compile($env) {
+ $a = $this->lvalue->compile($env);
+ $b = $this->rvalue->compile($env);
+
+ switch( $this->op ){
+ case 'and':
+ $result = $a && $b;
+ break;
+
+ case 'or':
+ $result = $a || $b;
+ break;
+
+ default:
+ if( Less_Parser::is_method($a, 'compare') ){
+ $result = $a->compare($b);
+ }elseif( Less_Parser::is_method($b, 'compare') ){
+ $result = $b->compare($a);
+ }else{
+ throw new Less_Exception_Compiler('Unable to perform comparison', null, $this->index);
+ }
+
+ switch ($result) {
+ case -1:
+ $result = $this->op === '<' || $this->op === '=<' || $this->op === '<=';
+ break;
+
+ case 0:
+ $result = $this->op === '=' || $this->op === '>=' || $this->op === '=<' || $this->op === '<=';
+ break;
+
+ case 1:
+ $result = $this->op === '>' || $this->op === '>=';
+ break;
+ }
+ break;
+ }
+
+ return $this->negate ? !$result : $result;
+ }
+
+}
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/DefaultFunc.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/DefaultFunc.php
new file mode 100644
index 000000000..c2dbf7496
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/DefaultFunc.php
@@ -0,0 +1,34 @@
+ruleset = $ruleset;
+ $this->frames = $frames;
+ }
+
+ public function accept($visitor) {
+ $this->ruleset = $visitor->visitObj($this->ruleset);
+ }
+
+ public function compile($env){
+ if( $this->frames ){
+ $frames = $this->frames;
+ }else{
+ $frames = $env->frames;
+ }
+ return new Less_Tree_DetachedRuleset($this->ruleset, $frames);
+ }
+
+ public function callEval($env) {
+ if( $this->frames ){
+ return $this->ruleset->compile( $env->copyEvalEnv( array_merge($this->frames,$env->frames) ) );
+ }
+ return $this->ruleset->compile( $env );
+ }
+}
+
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Dimension.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Dimension.php
new file mode 100644
index 000000000..2bfb9d54b
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Dimension.php
@@ -0,0 +1,201 @@
+value = floatval($value);
+
+ if( $unit && ($unit instanceof Less_Tree_Unit) ){
+ $this->unit = $unit;
+ }elseif( $unit ){
+ $this->unit = new Less_Tree_Unit( array($unit) );
+ }else{
+ $this->unit = new Less_Tree_Unit( );
+ }
+ }
+
+ public function accept( $visitor ){
+ $this->unit = $visitor->visitObj( $this->unit );
+ }
+
+ public function compile(){
+ return $this;
+ }
+
+ public function toColor() {
+ return new Less_Tree_Color(array($this->value, $this->value, $this->value));
+ }
+
+ /**
+ * @see Less_Tree::genCSS
+ */
+ public function genCSS( $output ){
+
+ if( Less_Parser::$options['strictUnits'] && !$this->unit->isSingular() ){
+ throw new Less_Exception_Compiler("Multiple units in dimension. Correct the units or use the unit function. Bad unit: ".$this->unit->toString());
+ }
+
+ $value = Less_Functions::fround( $this->value );
+ $strValue = (string)$value;
+
+ if( $value !== 0 && $value < 0.000001 && $value > -0.000001 ){
+ // would be output 1e-6 etc.
+ $strValue = number_format($strValue,10);
+ $strValue = preg_replace('/\.?0+$/','', $strValue);
+ }
+
+ if( Less_Parser::$options['compress'] ){
+ // Zero values doesn't need a unit
+ if( $value === 0 && $this->unit->isLength() ){
+ $output->add( $strValue );
+ return $strValue;
+ }
+
+ // Float values doesn't need a leading zero
+ if( $value > 0 && $value < 1 && $strValue[0] === '0' ){
+ $strValue = substr($strValue,1);
+ }
+ }
+
+ $output->add( $strValue );
+ $this->unit->genCSS( $output );
+ }
+
+ 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`.
+
+ /**
+ * @param string $op
+ */
+ public function operate( $op, $other){
+
+ $value = Less_Functions::operate( $op, $this->value, $other->value);
+ $unit = clone $this->unit;
+
+ if( $op === '+' || $op === '-' ){
+
+ if( !$unit->numerator && !$unit->denominator ){
+ $unit->numerator = $other->unit->numerator;
+ $unit->denominator = $other->unit->denominator;
+ }elseif( !$other->unit->numerator && !$other->unit->denominator ){
+ // do nothing
+ }else{
+ $other = $other->convertTo( $this->unit->usedUnits());
+
+ if( Less_Parser::$options['strictUnits'] && $other->unit->toString() !== $unit->toCSS() ){
+ throw new Less_Exception_Compiler("Incompatible units. Change the units or use the unit function. Bad units: '" . $unit->toString() . "' and " . $other->unit->toString() . "'.");
+ }
+
+ $value = Less_Functions::operate( $op, $this->value, $other->value);
+ }
+ }elseif( $op === '*' ){
+ $unit->numerator = array_merge($unit->numerator, $other->unit->numerator);
+ $unit->denominator = array_merge($unit->denominator, $other->unit->denominator);
+ sort($unit->numerator);
+ sort($unit->denominator);
+ $unit->cancel();
+ }elseif( $op === '/' ){
+ $unit->numerator = array_merge($unit->numerator, $other->unit->denominator);
+ $unit->denominator = array_merge($unit->denominator, $other->unit->numerator);
+ sort($unit->numerator);
+ sort($unit->denominator);
+ $unit->cancel();
+ }
+ return new Less_Tree_Dimension( $value, $unit);
+ }
+
+ public function compare($other) {
+ if ($other instanceof Less_Tree_Dimension) {
+
+ if( $this->unit->isEmpty() || $other->unit->isEmpty() ){
+ $a = $this;
+ $b = $other;
+ } else {
+ $a = $this->unify();
+ $b = $other->unify();
+ if( $a->unit->compare($b->unit) !== 0 ){
+ return -1;
+ }
+ }
+ $aValue = $a->value;
+ $bValue = $b->value;
+
+ if ($bValue > $aValue) {
+ return -1;
+ } elseif ($bValue < $aValue) {
+ return 1;
+ } else {
+ return 0;
+ }
+ } else {
+ return -1;
+ }
+ }
+
+ public function unify() {
+ return $this->convertTo(array('length'=> 'px', 'duration'=> 's', 'angle' => 'rad' ));
+ }
+
+ public function convertTo($conversions) {
+ $value = $this->value;
+ $unit = clone $this->unit;
+
+ if( is_string($conversions) ){
+ $derivedConversions = array();
+ foreach( Less_Tree_UnitConversions::$groups as $i ){
+ if( isset(Less_Tree_UnitConversions::${$i}[$conversions]) ){
+ $derivedConversions = array( $i => $conversions);
+ }
+ }
+ $conversions = $derivedConversions;
+ }
+
+
+ foreach($conversions as $groupName => $targetUnit){
+ $group = Less_Tree_UnitConversions::${$groupName};
+
+ //numerator
+ foreach($unit->numerator as $i => $atomicUnit){
+ $atomicUnit = $unit->numerator[$i];
+ if( !isset($group[$atomicUnit]) ){
+ continue;
+ }
+
+ $value = $value * ($group[$atomicUnit] / $group[$targetUnit]);
+
+ $unit->numerator[$i] = $targetUnit;
+ }
+
+ //denominator
+ foreach($unit->denominator as $i => $atomicUnit){
+ $atomicUnit = $unit->denominator[$i];
+ if( !isset($group[$atomicUnit]) ){
+ continue;
+ }
+
+ $value = $value / ($group[$atomicUnit] / $group[$targetUnit]);
+
+ $unit->denominator[$i] = $targetUnit;
+ }
+ }
+
+ $unit->cancel();
+
+ return new Less_Tree_Dimension( $value, $unit);
+ }
+}
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Directive.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Directive.php
new file mode 100644
index 000000000..04a1e467e
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Directive.php
@@ -0,0 +1,100 @@
+name = $name;
+ $this->value = $value;
+ if( $rules ){
+ $this->rules = $rules;
+ $this->rules->allowImports = true;
+ }
+
+ $this->index = $index;
+ $this->currentFileInfo = $currentFileInfo;
+ $this->debugInfo = $debugInfo;
+ }
+
+
+ public function accept( $visitor ){
+ if( $this->rules ){
+ $this->rules = $visitor->visitObj( $this->rules );
+ }
+ if( $this->value ){
+ $this->value = $visitor->visitObj( $this->value );
+ }
+ }
+
+
+ /**
+ * @see Less_Tree::genCSS
+ */
+ public function genCSS( $output ){
+ $value = $this->value;
+ $rules = $this->rules;
+ $output->add( $this->name, $this->currentFileInfo, $this->index );
+ if( $this->value ){
+ $output->add(' ');
+ $this->value->genCSS($output);
+ }
+ if( $this->rules ){
+ Less_Tree::outputRuleset( $output, array($this->rules));
+ } else {
+ $output->add(';');
+ }
+ }
+
+ public function compile($env){
+
+ $value = $this->value;
+ $rules = $this->rules;
+ if( $value ){
+ $value = $value->compile($env);
+ }
+
+ if( $rules ){
+ $rules = $rules->compile($env);
+ $rules->root = true;
+ }
+
+ return new Less_Tree_Directive( $this->name, $value, $rules, $this->index, $this->currentFileInfo, $this->debugInfo );
+ }
+
+
+ public function variable($name){
+ if( $this->rules ){
+ return $this->rules->variable($name);
+ }
+ }
+
+ public function find($selector){
+ if( $this->rules ){
+ return $this->rules->find($selector, $this);
+ }
+ }
+
+ //rulesets: function () { if (this.rules) return tree.Ruleset.prototype.rulesets.apply(this.rules); },
+
+ public function markReferenced(){
+ $this->isReferenced = true;
+ if( $this->rules ){
+ Less_Tree::ReferencedArray($this->rules->rules);
+ }
+ }
+
+}
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Element.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Element.php
new file mode 100644
index 000000000..9cea5e43c
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Element.php
@@ -0,0 +1,75 @@
+value = $value;
+ $this->value_is_object = is_object($value);
+
+ if( $combinator ){
+ $this->combinator = $combinator;
+ }
+
+ $this->index = $index;
+ $this->currentFileInfo = $currentFileInfo;
+ }
+
+ public function accept( $visitor ){
+ if( $this->value_is_object ){ //object or string
+ $this->value = $visitor->visitObj( $this->value );
+ }
+ }
+
+ public function compile($env){
+
+ if( Less_Environment::$mixin_stack ){
+ return new Less_Tree_Element($this->combinator, ($this->value_is_object ? $this->value->compile($env) : $this->value), $this->index, $this->currentFileInfo );
+ }
+
+ if( $this->value_is_object ){
+ $this->value = $this->value->compile($env);
+ }
+
+ return $this;
+ }
+
+ /**
+ * @see Less_Tree::genCSS
+ */
+ public function genCSS( $output ){
+ $output->add( $this->toCSS(), $this->currentFileInfo, $this->index );
+ }
+
+ public function toCSS(){
+
+ if( $this->value_is_object ){
+ $value = $this->value->toCSS();
+ }else{
+ $value = $this->value;
+ }
+
+
+ if( $value === '' && $this->combinator && $this->combinator === '&' ){
+ return '';
+ }
+
+
+ return Less_Environment::$_outputMap[$this->combinator] . $value;
+ }
+
+}
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Expression.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Expression.php
new file mode 100644
index 000000000..d834354a1
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Expression.php
@@ -0,0 +1,97 @@
+value = $value;
+ $this->parens = $parens;
+ }
+
+ public function accept( $visitor ){
+ $this->value = $visitor->visitArray( $this->value );
+ }
+
+ public function compile($env) {
+
+ $doubleParen = false;
+
+ if( $this->parens && !$this->parensInOp ){
+ Less_Environment::$parensStack++;
+ }
+
+ $returnValue = null;
+ if( $this->value ){
+
+ $count = count($this->value);
+
+ if( $count > 1 ){
+
+ $ret = array();
+ foreach($this->value as $e){
+ $ret[] = $e->compile($env);
+ }
+ $returnValue = new Less_Tree_Expression($ret);
+
+ }else{
+
+ if( ($this->value[0] instanceof Less_Tree_Expression) && $this->value[0]->parens && !$this->value[0]->parensInOp ){
+ $doubleParen = true;
+ }
+
+ $returnValue = $this->value[0]->compile($env);
+ }
+
+ } else {
+ $returnValue = $this;
+ }
+
+ if( $this->parens ){
+ if( !$this->parensInOp ){
+ Less_Environment::$parensStack--;
+
+ }elseif( !Less_Environment::isMathOn() && !$doubleParen ){
+ $returnValue = new Less_Tree_Paren($returnValue);
+
+ }
+ }
+ return $returnValue;
+ }
+
+ /**
+ * @see Less_Tree::genCSS
+ */
+ public function genCSS( $output ){
+ $val_len = count($this->value);
+ for( $i = 0; $i < $val_len; $i++ ){
+ $this->value[$i]->genCSS( $output );
+ if( $i + 1 < $val_len ){
+ $output->add( ' ' );
+ }
+ }
+ }
+
+ public function throwAwayComments() {
+
+ if( is_array($this->value) ){
+ $new_value = array();
+ foreach($this->value as $v){
+ if( $v instanceof Less_Tree_Comment ){
+ continue;
+ }
+ $new_value[] = $v;
+ }
+ $this->value = $new_value;
+ }
+ }
+}
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Extend.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Extend.php
new file mode 100644
index 000000000..8f21e939d
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Extend.php
@@ -0,0 +1,77 @@
+selector = $selector;
+ $this->option = $option;
+ $this->index = $index;
+
+ switch($option){
+ case "all":
+ $this->allowBefore = true;
+ $this->allowAfter = true;
+ break;
+ default:
+ $this->allowBefore = false;
+ $this->allowAfter = false;
+ break;
+ }
+
+ $this->object_id = $i++;
+ $this->parent_ids = array($this->object_id);
+ }
+
+ public function accept( $visitor ){
+ $this->selector = $visitor->visitObj( $this->selector );
+ }
+
+ public function compile( $env ){
+ Less_Parser::$has_extends = true;
+ $this->selector = $this->selector->compile($env);
+ return $this;
+ //return new Less_Tree_Extend( $this->selector->compile($env), $this->option, $this->index);
+ }
+
+ public function findSelfSelectors( $selectors ){
+ $selfElements = array();
+
+
+ for( $i = 0, $selectors_len = count($selectors); $i < $selectors_len; $i++ ){
+ $selectorElements = $selectors[$i]->elements;
+ // duplicate the logic in genCSS function inside the selector node.
+ // future TODO - move both logics into the selector joiner visitor
+ if( $i && $selectorElements && $selectorElements[0]->combinator === "") {
+ $selectorElements[0]->combinator = ' ';
+ }
+ $selfElements = array_merge( $selfElements, $selectors[$i]->elements );
+ }
+
+ $this->selfSelectors = array(new Less_Tree_Selector($selfElements));
+ }
+
+}
\ No newline at end of file
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Import.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Import.php
new file mode 100644
index 000000000..4e14afe7e
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Import.php
@@ -0,0 +1,304 @@
+options = $options;
+ $this->index = $index;
+ $this->path = $path;
+ $this->features = $features;
+ $this->currentFileInfo = $currentFileInfo;
+
+ if( is_array($options) ){
+ $this->options += array('inline'=>false);
+
+ if( isset($this->options['less']) || $this->options['inline'] ){
+ $this->css = !isset($this->options['less']) || !$this->options['less'] || $this->options['inline'];
+ } else {
+ $pathValue = $this->getPath();
+ if( $pathValue && preg_match('/css([\?;].*)?$/',$pathValue) ){
+ $this->css = true;
+ }
+ }
+ }
+ }
+
+//
+// The actual import node doesn't return anything, when converted to CSS.
+// The reason is that it's used at the evaluation stage, so that the rules
+// it imports can be treated like any other rules.
+//
+// In `eval`, we make sure all Import nodes get evaluated, recursively, so
+// we end up with a flat structure, which can easily be imported in the parent
+// ruleset.
+//
+
+ public function accept($visitor){
+
+ if( $this->features ){
+ $this->features = $visitor->visitObj($this->features);
+ }
+ $this->path = $visitor->visitObj($this->path);
+
+ if( !$this->options['inline'] && $this->root ){
+ $this->root = $visitor->visit($this->root);
+ }
+ }
+
+ /**
+ * @see Less_Tree::genCSS
+ */
+ public function genCSS( $output ){
+ if( $this->css ){
+
+ $output->add( '@import ', $this->currentFileInfo, $this->index );
+
+ $this->path->genCSS( $output );
+ if( $this->features ){
+ $output->add( ' ' );
+ $this->features->genCSS( $output );
+ }
+ $output->add( ';' );
+ }
+ }
+
+ public function toCSS(){
+ $features = $this->features ? ' ' . $this->features->toCSS() : '';
+
+ if ($this->css) {
+ return "@import " . $this->path->toCSS() . $features . ";\n";
+ } else {
+ return "";
+ }
+ }
+
+ /**
+ * @return string
+ */
+ public function getPath(){
+ if ($this->path instanceof Less_Tree_Quoted) {
+ $path = $this->path->value;
+ $path = ( isset($this->css) || preg_match('/(\.[a-z]*$)|([\?;].*)$/',$path)) ? $path : $path . '.less';
+ } else if ($this->path instanceof Less_Tree_URL) {
+ $path = $this->path->value->value;
+ }else{
+ return null;
+ }
+
+ //remove query string and fragment
+ return preg_replace('/[\?#][^\?]*$/','',$path);
+ }
+
+ public function compileForImport( $env ){
+ return new Less_Tree_Import( $this->path->compile($env), $this->features, $this->options, $this->index, $this->currentFileInfo);
+ }
+
+ public function compilePath($env) {
+ $path = $this->path->compile($env);
+ $rootpath = '';
+ if( $this->currentFileInfo && $this->currentFileInfo['rootpath'] ){
+ $rootpath = $this->currentFileInfo['rootpath'];
+ }
+
+
+ if( !($path instanceof Less_Tree_URL) ){
+ if( $rootpath ){
+ $pathValue = $path->value;
+ // Add the base path if the import is relative
+ if( $pathValue && Less_Environment::isPathRelative($pathValue) ){
+ $path->value = $this->currentFileInfo['uri_root'].$pathValue;
+ }
+ }
+ $path->value = Less_Environment::normalizePath($path->value);
+ }
+
+
+
+ return $path;
+ }
+
+ public function compile( $env ){
+
+ $evald = $this->compileForImport($env);
+
+ //get path & uri
+ $path_and_uri = null;
+ if( is_callable(Less_Parser::$options['import_callback']) ){
+ $path_and_uri = call_user_func(Less_Parser::$options['import_callback'],$evald);
+ }
+
+ if( !$path_and_uri ){
+ $path_and_uri = $evald->PathAndUri();
+ }
+
+ if( $path_and_uri ){
+ list($full_path, $uri) = $path_and_uri;
+ }else{
+ $full_path = $uri = $evald->getPath();
+ }
+
+
+ //import once
+ if( $evald->skip( $full_path, $env) ){
+ return array();
+ }
+
+ if( $this->options['inline'] ){
+ //todo needs to reference css file not import
+ //$contents = new Less_Tree_Anonymous($this->root, 0, array('filename'=>$this->importedFilename), true );
+
+ Less_Parser::AddParsedFile($full_path);
+ $contents = new Less_Tree_Anonymous( file_get_contents($full_path), 0, array(), true );
+
+ if( $this->features ){
+ return new Less_Tree_Media( array($contents), $this->features->value );
+ }
+
+ return array( $contents );
+ }
+
+ // optional (need to be before "CSS" to support optional CSS imports. CSS should be checked only if empty($this->currentFileInfo))
+ if( isset($this->options['optional']) && $this->options['optional'] && !file_exists($full_path) && (!$evald->css || !empty($this->currentFileInfo))) {
+ return array();
+ }
+
+
+ // css ?
+ if( $evald->css ){
+ $features = ( $evald->features ? $evald->features->compile($env) : null );
+ return new Less_Tree_Import( $this->compilePath( $env), $features, $this->options, $this->index);
+ }
+
+
+ return $this->ParseImport( $full_path, $uri, $env );
+ }
+
+
+ /**
+ * Using the import directories, get the full absolute path and uri of the import
+ *
+ * @param Less_Tree_Import $evald
+ */
+ public function PathAndUri(){
+
+ $evald_path = $this->getPath();
+
+ if( $evald_path ){
+
+ $import_dirs = array();
+
+ if( Less_Environment::isPathRelative($evald_path) ){
+ //if the path is relative, the file should be in the current directory
+ $import_dirs[ $this->currentFileInfo['currentDirectory'] ] = $this->currentFileInfo['uri_root'];
+
+ }else{
+ //otherwise, the file should be relative to the server root
+ $import_dirs[ $this->currentFileInfo['entryPath'] ] = $this->currentFileInfo['entryUri'];
+
+ //if the user supplied entryPath isn't the actual root
+ $import_dirs[ $_SERVER['DOCUMENT_ROOT'] ] = '';
+
+ }
+
+ // always look in user supplied import directories
+ $import_dirs = array_merge( $import_dirs, Less_Parser::$options['import_dirs'] );
+
+
+ foreach( $import_dirs as $rootpath => $rooturi){
+ if( is_callable($rooturi) ){
+ list($path, $uri) = call_user_func($rooturi, $evald_path);
+ if( is_string($path) ){
+ $full_path = $path;
+ return array( $full_path, $uri );
+ }
+ }elseif( !empty($rootpath) ){
+
+ $path = rtrim($rootpath,'/\\').'/'.ltrim($evald_path,'/\\');
+
+ if( file_exists($path) ){
+ $full_path = Less_Environment::normalizePath($path);
+ $uri = Less_Environment::normalizePath(dirname($rooturi.$evald_path));
+ return array( $full_path, $uri );
+ } elseif( file_exists($path.'.less') ){
+ $full_path = Less_Environment::normalizePath($path.'.less');
+ $uri = Less_Environment::normalizePath(dirname($rooturi.$evald_path.'.less'));
+ return array( $full_path, $uri );
+ }
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Parse the import url and return the rules
+ *
+ * @return Less_Tree_Media|array
+ */
+ public function ParseImport( $full_path, $uri, $env ){
+
+ $import_env = clone $env;
+ if( (isset($this->options['reference']) && $this->options['reference']) || isset($this->currentFileInfo['reference']) ){
+ $import_env->currentFileInfo['reference'] = true;
+ }
+
+ if( (isset($this->options['multiple']) && $this->options['multiple']) ){
+ $import_env->importMultiple = true;
+ }
+
+ $parser = new Less_Parser($import_env);
+ $root = $parser->parseFile($full_path, $uri, true);
+
+
+ $ruleset = new Less_Tree_Ruleset(array(), $root->rules );
+ $ruleset->evalImports($import_env);
+
+ return $this->features ? new Less_Tree_Media($ruleset->rules, $this->features->value) : $ruleset->rules;
+ }
+
+
+ /**
+ * Should the import be skipped?
+ *
+ * @return boolean|null
+ */
+ private function Skip($path, $env){
+
+ $path = Less_Parser::AbsPath($path, true);
+
+ if( $path && Less_Parser::FileParsed($path) ){
+
+ if( isset($this->currentFileInfo['reference']) ){
+ return true;
+ }
+
+ return !isset($this->options['multiple']) && !$env->importMultiple;
+ }
+
+ }
+}
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Javascript.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Javascript.php
new file mode 100644
index 000000000..1b03183d9
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Javascript.php
@@ -0,0 +1,30 @@
+escaped = $escaped;
+ $this->expression = $string;
+ $this->index = $index;
+ }
+
+ public function compile(){
+ return new Less_Tree_Anonymous('/* Sorry, can not do JavaScript evaluation in PHP... :( */');
+ }
+
+}
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Keyword.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Keyword.php
new file mode 100644
index 000000000..e1d98c456
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Keyword.php
@@ -0,0 +1,44 @@
+value = $value;
+ }
+
+ public function compile(){
+ return $this;
+ }
+
+ /**
+ * @see Less_Tree::genCSS
+ */
+ public function genCSS( $output ){
+
+ if( $this->value === '%') {
+ throw new Less_Exception_Compiler("Invalid % without number");
+ }
+
+ $output->add( $this->value );
+ }
+
+ public function compare($other) {
+ if ($other instanceof Less_Tree_Keyword) {
+ return $other->value === $this->value ? 0 : 1;
+ } else {
+ return -1;
+ }
+ }
+}
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Media.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Media.php
new file mode 100644
index 000000000..f9ee9d421
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Media.php
@@ -0,0 +1,179 @@
+index = $index;
+ $this->currentFileInfo = $currentFileInfo;
+
+ $selectors = $this->emptySelectors();
+
+ $this->features = new Less_Tree_Value($features);
+
+ $this->rules = array(new Less_Tree_Ruleset($selectors, $value));
+ $this->rules[0]->allowImports = true;
+ }
+
+ public function accept( $visitor ){
+ $this->features = $visitor->visitObj($this->features);
+ $this->rules = $visitor->visitArray($this->rules);
+ }
+
+ /**
+ * @see Less_Tree::genCSS
+ */
+ public function genCSS( $output ){
+
+ $output->add( '@media ', $this->currentFileInfo, $this->index );
+ $this->features->genCSS( $output );
+ Less_Tree::outputRuleset( $output, $this->rules);
+
+ }
+
+ public function compile($env) {
+
+ $media = new Less_Tree_Media(array(), array(), $this->index, $this->currentFileInfo );
+
+ $strictMathBypass = false;
+ if( Less_Parser::$options['strictMath'] === false) {
+ $strictMathBypass = true;
+ Less_Parser::$options['strictMath'] = true;
+ }
+
+ $media->features = $this->features->compile($env);
+
+ if( $strictMathBypass ){
+ Less_Parser::$options['strictMath'] = false;
+ }
+
+ $env->mediaPath[] = $media;
+ $env->mediaBlocks[] = $media;
+
+ array_unshift($env->frames, $this->rules[0]);
+ $media->rules = array($this->rules[0]->compile($env));
+ array_shift($env->frames);
+
+ array_pop($env->mediaPath);
+
+ return !$env->mediaPath ? $media->compileTop($env) : $media->compileNested($env);
+ }
+
+ public function variable($name) {
+ return $this->rules[0]->variable($name);
+ }
+
+ public function find($selector) {
+ return $this->rules[0]->find($selector, $this);
+ }
+
+ public function emptySelectors(){
+ $el = new Less_Tree_Element('','&', $this->index, $this->currentFileInfo );
+ $sels = array( new Less_Tree_Selector(array($el), array(), null, $this->index, $this->currentFileInfo) );
+ $sels[0]->mediaEmpty = true;
+ return $sels;
+ }
+
+ public function markReferenced(){
+ $this->rules[0]->markReferenced();
+ $this->isReferenced = true;
+ Less_Tree::ReferencedArray($this->rules[0]->rules);
+ }
+
+ // evaltop
+ public function compileTop($env) {
+ $result = $this;
+
+ if (count($env->mediaBlocks) > 1) {
+ $selectors = $this->emptySelectors();
+ $result = new Less_Tree_Ruleset($selectors, $env->mediaBlocks);
+ $result->multiMedia = true;
+ }
+
+ $env->mediaBlocks = array();
+ $env->mediaPath = array();
+
+ return $result;
+ }
+
+ public function compileNested($env) {
+ $path = array_merge($env->mediaPath, array($this));
+
+ // Extract the media-query conditions separated with `,` (OR).
+ foreach ($path as $key => $p) {
+ $value = $p->features instanceof Less_Tree_Value ? $p->features->value : $p->features;
+ $path[$key] = is_array($value) ? $value : array($value);
+ }
+
+ // Trace all permutations to generate the resulting media-query.
+ //
+ // (a, b and c) with nested (d, e) ->
+ // a and d
+ // a and e
+ // b and c and d
+ // b and c and e
+
+ $permuted = $this->permute($path);
+ $expressions = array();
+ foreach($permuted as $path){
+
+ for( $i=0, $len=count($path); $i < $len; $i++){
+ $path[$i] = Less_Parser::is_method($path[$i], 'toCSS') ? $path[$i] : new Less_Tree_Anonymous($path[$i]);
+ }
+
+ for( $i = count($path) - 1; $i > 0; $i-- ){
+ array_splice($path, $i, 0, array(new Less_Tree_Anonymous('and')));
+ }
+
+ $expressions[] = new Less_Tree_Expression($path);
+ }
+ $this->features = new Less_Tree_Value($expressions);
+
+
+
+ // Fake a tree-node that doesn't output anything.
+ return new Less_Tree_Ruleset(array(), array());
+ }
+
+ public function permute($arr) {
+ if (!$arr)
+ return array();
+
+ if (count($arr) == 1)
+ return $arr[0];
+
+ $result = array();
+ $rest = $this->permute(array_slice($arr, 1));
+ foreach ($rest as $r) {
+ foreach ($arr[0] as $a) {
+ $result[] = array_merge(
+ is_array($a) ? $a : array($a),
+ is_array($r) ? $r : array($r)
+ );
+ }
+ }
+
+ return $result;
+ }
+
+ public function bubbleSelectors($selectors) {
+
+ if( !$selectors) return;
+
+ $this->rules = array(new Less_Tree_Ruleset( $selectors, array($this->rules[0])));
+ }
+
+}
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Mixin/Call.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Mixin/Call.php
new file mode 100644
index 000000000..04eb426d9
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Mixin/Call.php
@@ -0,0 +1,202 @@
+selector = new Less_Tree_Selector($elements);
+ $this->arguments = $args;
+ $this->index = $index;
+ $this->currentFileInfo = $currentFileInfo;
+ $this->important = $important;
+ }
+
+ //function accept($visitor){
+ // $this->selector = $visitor->visit($this->selector);
+ // $this->arguments = $visitor->visit($this->arguments);
+ //}
+
+
+ public function compile($env){
+
+ $rules = array();
+ $match = false;
+ $isOneFound = false;
+ $candidates = array();
+ $defaultUsed = false;
+ $conditionResult = array();
+
+ $args = array();
+ foreach($this->arguments as $a){
+ $args[] = array('name'=> $a['name'], 'value' => $a['value']->compile($env) );
+ }
+
+ foreach($env->frames as $frame){
+
+ $mixins = $frame->find($this->selector);
+
+ if( !$mixins ){
+ continue;
+ }
+
+ $isOneFound = true;
+ $defNone = 0;
+ $defTrue = 1;
+ $defFalse = 2;
+
+ // To make `default()` function independent of definition order we have two "subpasses" here.
+ // At first we evaluate each guard *twice* (with `default() == true` and `default() == false`),
+ // and build candidate list with corresponding flags. Then, when we know all possible matches,
+ // we make a final decision.
+
+ $mixins_len = count($mixins);
+ for( $m = 0; $m < $mixins_len; $m++ ){
+ $mixin = $mixins[$m];
+
+ if( $this->IsRecursive( $env, $mixin ) ){
+ continue;
+ }
+
+ if( $mixin->matchArgs($args, $env) ){
+
+ $candidate = array('mixin' => $mixin, 'group' => $defNone);
+
+ if( $mixin instanceof Less_Tree_Ruleset ){
+
+ for( $f = 0; $f < 2; $f++ ){
+ Less_Tree_DefaultFunc::value($f);
+ $conditionResult[$f] = $mixin->matchCondition( $args, $env);
+ }
+ if( $conditionResult[0] || $conditionResult[1] ){
+ if( $conditionResult[0] != $conditionResult[1] ){
+ $candidate['group'] = $conditionResult[1] ? $defTrue : $defFalse;
+ }
+
+ $candidates[] = $candidate;
+ }
+ }else{
+ $candidates[] = $candidate;
+ }
+
+ $match = true;
+ }
+ }
+
+ Less_Tree_DefaultFunc::reset();
+
+
+ $count = array(0, 0, 0);
+ for( $m = 0; $m < count($candidates); $m++ ){
+ $count[ $candidates[$m]['group'] ]++;
+ }
+
+ if( $count[$defNone] > 0 ){
+ $defaultResult = $defFalse;
+ } else {
+ $defaultResult = $defTrue;
+ if( ($count[$defTrue] + $count[$defFalse]) > 1 ){
+ throw new Exception( 'Ambiguous use of `default()` found when matching for `' . $this->format($args) . '`' );
+ }
+ }
+
+
+ $candidates_length = count($candidates);
+ $length_1 = ($candidates_length == 1);
+
+ for( $m = 0; $m < $candidates_length; $m++){
+ $candidate = $candidates[$m]['group'];
+ if( ($candidate === $defNone) || ($candidate === $defaultResult) ){
+ try{
+ $mixin = $candidates[$m]['mixin'];
+ if( !($mixin instanceof Less_Tree_Mixin_Definition) ){
+ $mixin = new Less_Tree_Mixin_Definition('', array(), $mixin->rules, null, false);
+ $mixin->originalRuleset = $mixins[$m]->originalRuleset;
+ }
+ $rules = array_merge($rules, $mixin->evalCall($env, $args, $this->important)->rules);
+ } catch (Exception $e) {
+ //throw new Less_Exception_Compiler($e->getMessage(), $e->index, null, $this->currentFileInfo['filename']);
+ throw new Less_Exception_Compiler($e->getMessage(), null, null, $this->currentFileInfo);
+ }
+ }
+ }
+
+ if( $match ){
+ if( !$this->currentFileInfo || !isset($this->currentFileInfo['reference']) || !$this->currentFileInfo['reference'] ){
+ Less_Tree::ReferencedArray($rules);
+ }
+
+ return $rules;
+ }
+ }
+
+ if( $isOneFound ){
+ throw new Less_Exception_Compiler('No matching definition was found for `'.$this->Format( $args ).'`', null, $this->index, $this->currentFileInfo);
+
+ }else{
+ throw new Less_Exception_Compiler(trim($this->selector->toCSS()) . " is undefined in ".$this->currentFileInfo['filename'], null, $this->index);
+ }
+
+ }
+
+ /**
+ * Format the args for use in exception messages
+ *
+ */
+ private function Format($args){
+ $message = array();
+ if( $args ){
+ foreach($args as $a){
+ $argValue = '';
+ if( $a['name'] ){
+ $argValue .= $a['name'] . ':';
+ }
+ if( is_object($a['value']) ){
+ $argValue .= $a['value']->toCSS();
+ }else{
+ $argValue .= '???';
+ }
+ $message[] = $argValue;
+ }
+ }
+ return implode(', ',$message);
+ }
+
+
+ /**
+ * Are we in a recursive mixin call?
+ *
+ * @return bool
+ */
+ private function IsRecursive( $env, $mixin ){
+
+ foreach($env->frames as $recur_frame){
+ if( !($mixin instanceof Less_Tree_Mixin_Definition) ){
+
+ if( $mixin === $recur_frame ){
+ return true;
+ }
+
+ if( isset($recur_frame->originalRuleset) && $mixin->ruleset_id === $recur_frame->originalRuleset ){
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+}
+
+
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Mixin/Definition.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Mixin/Definition.php
new file mode 100644
index 000000000..b16d68871
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Mixin/Definition.php
@@ -0,0 +1,241 @@
+name = $name;
+ $this->selectors = array(new Less_Tree_Selector(array( new Less_Tree_Element(null, $name))));
+
+ $this->params = $params;
+ $this->condition = $condition;
+ $this->variadic = $variadic;
+ $this->rules = $rules;
+
+ if( $params ){
+ $this->arity = count($params);
+ foreach( $params as $p ){
+ if (! isset($p['name']) || ($p['name'] && !isset($p['value']))) {
+ $this->required++;
+ }
+ }
+ }
+
+ $this->frames = $frames;
+ $this->SetRulesetIndex();
+ }
+
+
+
+ //function accept( $visitor ){
+ // $this->params = $visitor->visit($this->params);
+ // $this->rules = $visitor->visit($this->rules);
+ // $this->condition = $visitor->visit($this->condition);
+ //}
+
+
+ public function toCSS(){
+ return '';
+ }
+
+ // less.js : /lib/less/tree/mixin.js : tree.mixin.Definition.evalParams
+ public function compileParams($env, $mixinFrames, $args = array() , &$evaldArguments = array() ){
+ $frame = new Less_Tree_Ruleset(null, array());
+ $params = $this->params;
+ $mixinEnv = null;
+ $argsLength = 0;
+
+ if( $args ){
+ $argsLength = count($args);
+ for($i = 0; $i < $argsLength; $i++ ){
+ $arg = $args[$i];
+
+ if( $arg && $arg['name'] ){
+ $isNamedFound = false;
+
+ foreach($params as $j => $param){
+ if( !isset($evaldArguments[$j]) && $arg['name'] === $params[$j]['name']) {
+ $evaldArguments[$j] = $arg['value']->compile($env);
+ array_unshift($frame->rules, new Less_Tree_Rule( $arg['name'], $arg['value']->compile($env) ) );
+ $isNamedFound = true;
+ break;
+ }
+ }
+ if ($isNamedFound) {
+ array_splice($args, $i, 1);
+ $i--;
+ $argsLength--;
+ continue;
+ } else {
+ throw new Less_Exception_Compiler("Named argument for " . $this->name .' '.$args[$i]['name'] . ' not found');
+ }
+ }
+ }
+ }
+
+ $argIndex = 0;
+ foreach($params as $i => $param){
+
+ if ( isset($evaldArguments[$i]) ){ continue; }
+
+ $arg = null;
+ if( isset($args[$argIndex]) ){
+ $arg = $args[$argIndex];
+ }
+
+ if (isset($param['name']) && $param['name']) {
+
+ if( isset($param['variadic']) ){
+ $varargs = array();
+ for ($j = $argIndex; $j < $argsLength; $j++) {
+ $varargs[] = $args[$j]['value']->compile($env);
+ }
+ $expression = new Less_Tree_Expression($varargs);
+ array_unshift($frame->rules, new Less_Tree_Rule($param['name'], $expression->compile($env)));
+ }else{
+ $val = ($arg && $arg['value']) ? $arg['value'] : false;
+
+ if ($val) {
+ $val = $val->compile($env);
+ } else if ( isset($param['value']) ) {
+
+ if( !$mixinEnv ){
+ $mixinEnv = new Less_Environment();
+ $mixinEnv->frames = array_merge( array($frame), $mixinFrames);
+ }
+
+ $val = $param['value']->compile($mixinEnv);
+ $frame->resetCache();
+ } else {
+ throw new Less_Exception_Compiler("Wrong number of arguments for " . $this->name . " (" . $argsLength . ' for ' . $this->arity . ")");
+ }
+
+ array_unshift($frame->rules, new Less_Tree_Rule($param['name'], $val));
+ $evaldArguments[$i] = $val;
+ }
+ }
+
+ if ( isset($param['variadic']) && $args) {
+ for ($j = $argIndex; $j < $argsLength; $j++) {
+ $evaldArguments[$j] = $args[$j]['value']->compile($env);
+ }
+ }
+ $argIndex++;
+ }
+
+ ksort($evaldArguments);
+ $evaldArguments = array_values($evaldArguments);
+
+ return $frame;
+ }
+
+ public function compile($env) {
+ if( $this->frames ){
+ return new Less_Tree_Mixin_Definition($this->name, $this->params, $this->rules, $this->condition, $this->variadic, $this->frames );
+ }
+ return new Less_Tree_Mixin_Definition($this->name, $this->params, $this->rules, $this->condition, $this->variadic, $env->frames );
+ }
+
+ public function evalCall($env, $args = NULL, $important = NULL) {
+
+ Less_Environment::$mixin_stack++;
+
+ $_arguments = array();
+
+ if( $this->frames ){
+ $mixinFrames = array_merge($this->frames, $env->frames);
+ }else{
+ $mixinFrames = $env->frames;
+ }
+
+ $frame = $this->compileParams($env, $mixinFrames, $args, $_arguments);
+
+ $ex = new Less_Tree_Expression($_arguments);
+ array_unshift($frame->rules, new Less_Tree_Rule('@arguments', $ex->compile($env)));
+
+
+ $ruleset = new Less_Tree_Ruleset(null, $this->rules);
+ $ruleset->originalRuleset = $this->ruleset_id;
+
+
+ $ruleSetEnv = new Less_Environment();
+ $ruleSetEnv->frames = array_merge( array($this, $frame), $mixinFrames );
+ $ruleset = $ruleset->compile( $ruleSetEnv );
+
+ if( $important ){
+ $ruleset = $ruleset->makeImportant();
+ }
+
+ Less_Environment::$mixin_stack--;
+
+ return $ruleset;
+ }
+
+
+ public function matchCondition($args, $env) {
+
+ if( !$this->condition ){
+ return true;
+ }
+
+ // set array to prevent error on array_merge
+ if(!is_array($this->frames)) {
+ $this->frames = array();
+ }
+
+ $frame = $this->compileParams($env, array_merge($this->frames,$env->frames), $args );
+
+ $compile_env = new Less_Environment();
+ $compile_env->frames = array_merge(
+ array($frame) // the parameter variables
+ , $this->frames // the parent namespace/mixin frames
+ , $env->frames // the current environment frames
+ );
+
+ $compile_env->functions = $env->functions;
+
+ return (bool)$this->condition->compile($compile_env);
+ }
+
+ public function matchArgs($args, $env = NULL){
+ $argsLength = count($args);
+
+ if( !$this->variadic ){
+ if( $argsLength < $this->required ){
+ return false;
+ }
+ if( $argsLength > count($this->params) ){
+ return false;
+ }
+ }else{
+ if( $argsLength < ($this->required - 1)){
+ return false;
+ }
+ }
+
+ $len = min($argsLength, $this->arity);
+
+ for( $i = 0; $i < $len; $i++ ){
+ if( !isset($this->params[$i]['name']) && !isset($this->params[$i]['variadic']) ){
+ if( $args[$i]['value']->compile($env)->toCSS() != $this->params[$i]['value']->compile($env)->toCSS() ){
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+}
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/NameValue.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/NameValue.php
new file mode 100644
index 000000000..31cbe03ed
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/NameValue.php
@@ -0,0 +1,51 @@
+ color:#FF0000;
+ *
+ * @package Less
+ * @subpackage tree
+ */
+class Less_Tree_NameValue extends Less_Tree{
+
+ public $name;
+ public $value;
+ public $index;
+ public $currentFileInfo;
+ public $type = 'NameValue';
+ public $important = '';
+
+ public function __construct($name, $value = null, $index = null, $currentFileInfo = null ){
+ $this->name = $name;
+ $this->value = $value;
+ $this->index = $index;
+ $this->currentFileInfo = $currentFileInfo;
+ }
+
+ public function genCSS( $output ){
+
+ $output->add(
+ $this->name
+ . Less_Environment::$_outputMap[': ']
+ . $this->value
+ . $this->important
+ . (((Less_Environment::$lastRule && Less_Parser::$options['compress'])) ? "" : ";")
+ , $this->currentFileInfo, $this->index);
+ }
+
+ public function compile ($env){
+ return $this;
+ }
+
+ public function makeImportant(){
+ $new = new Less_Tree_NameValue($this->name, $this->value, $this->index, $this->currentFileInfo);
+ $new->important = ' !important';
+ return $new;
+ }
+
+
+}
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Negative.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Negative.php
new file mode 100644
index 000000000..507443ecf
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Negative.php
@@ -0,0 +1,37 @@
+value = $node;
+ }
+
+ //function accept($visitor) {
+ // $this->value = $visitor->visit($this->value);
+ //}
+
+ /**
+ * @see Less_Tree::genCSS
+ */
+ public function genCSS( $output ){
+ $output->add( '-' );
+ $this->value->genCSS( $output );
+ }
+
+ public function compile($env) {
+ if( Less_Environment::isMathOn() ){
+ $ret = new Less_Tree_Operation('*', array( new Less_Tree_Dimension(-1), $this->value ) );
+ return $ret->compile($env);
+ }
+ return new Less_Tree_Negative( $this->value->compile($env) );
+ }
+}
\ No newline at end of file
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Operation.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Operation.php
new file mode 100644
index 000000000..e69e0da6e
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Operation.php
@@ -0,0 +1,70 @@
+op = trim($op);
+ $this->operands = $operands;
+ $this->isSpaced = $isSpaced;
+ }
+
+ public function accept($visitor) {
+ $this->operands = $visitor->visitArray($this->operands);
+ }
+
+ public function compile($env){
+ $a = $this->operands[0]->compile($env);
+ $b = $this->operands[1]->compile($env);
+
+
+ if( Less_Environment::isMathOn() ){
+
+ if( $a instanceof Less_Tree_Dimension && $b instanceof Less_Tree_Color ){
+ $a = $a->toColor();
+
+ }elseif( $b instanceof Less_Tree_Dimension && $a instanceof Less_Tree_Color ){
+ $b = $b->toColor();
+
+ }
+
+ if( !method_exists($a,'operate') ){
+ throw new Less_Exception_Compiler("Operation on an invalid type");
+ }
+
+ return $a->operate( $this->op, $b);
+ }
+
+ return new Less_Tree_Operation($this->op, array($a, $b), $this->isSpaced );
+ }
+
+
+ /**
+ * @see Less_Tree::genCSS
+ */
+ public function genCSS( $output ){
+ $this->operands[0]->genCSS( $output );
+ if( $this->isSpaced ){
+ $output->add( " " );
+ }
+ $output->add( $this->op );
+ if( $this->isSpaced ){
+ $output->add( ' ' );
+ }
+ $this->operands[1]->genCSS( $output );
+ }
+
+}
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Paren.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Paren.php
new file mode 100644
index 000000000..01864550a
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Paren.php
@@ -0,0 +1,35 @@
+value = $value;
+ }
+
+ public function accept($visitor){
+ $this->value = $visitor->visitObj($this->value);
+ }
+
+ /**
+ * @see Less_Tree::genCSS
+ */
+ public function genCSS( $output ){
+ $output->add( '(' );
+ $this->value->genCSS( $output );
+ $output->add( ')' );
+ }
+
+ public function compile($env) {
+ return new Less_Tree_Paren($this->value->compile($env));
+ }
+
+}
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Quoted.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Quoted.php
new file mode 100644
index 000000000..80063b5ef
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Quoted.php
@@ -0,0 +1,81 @@
+escaped = $escaped;
+ $this->value = $content;
+ if( $str ){
+ $this->quote = $str[0];
+ }
+ $this->index = $index;
+ $this->currentFileInfo = $currentFileInfo;
+ }
+
+ /**
+ * @see Less_Tree::genCSS
+ */
+ public function genCSS( $output ){
+ if( !$this->escaped ){
+ $output->add( $this->quote, $this->currentFileInfo, $this->index );
+ }
+ $output->add( $this->value );
+ if( !$this->escaped ){
+ $output->add( $this->quote );
+ }
+ }
+
+ public function compile($env){
+
+ $value = $this->value;
+ if( preg_match_all('/`([^`]+)`/', $this->value, $matches) ){
+ foreach($matches as $i => $match){
+ $js = new Less_Tree_JavaScript($matches[1], $this->index, true);
+ $js = $js->compile()->value;
+ $value = str_replace($matches[0][$i], $js, $value);
+ }
+ }
+
+ if( preg_match_all('/@\{([\w-]+)\}/',$value,$matches) ){
+ foreach($matches[1] as $i => $match){
+ $v = new Less_Tree_Variable('@' . $match, $this->index, $this->currentFileInfo );
+ $v = $v->compile($env);
+ $v = ($v instanceof Less_Tree_Quoted) ? $v->value : $v->toCSS();
+ $value = str_replace($matches[0][$i], $v, $value);
+ }
+ }
+
+ return new Less_Tree_Quoted($this->quote . $value . $this->quote, $value, $this->escaped, $this->index, $this->currentFileInfo);
+ }
+
+ public function compare($x) {
+
+ if( !Less_Parser::is_method($x, 'toCSS') ){
+ return -1;
+ }
+
+ $left = $this->toCSS();
+ $right = $x->toCSS();
+
+ if ($left === $right) {
+ return 0;
+ }
+
+ return $left < $right ? -1 : 1;
+ }
+}
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Rule.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Rule.php
new file mode 100644
index 000000000..ee4a9e257
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Rule.php
@@ -0,0 +1,115 @@
+name = $name;
+ $this->value = ($value instanceof Less_Tree_Value || $value instanceof Less_Tree_Ruleset) ? $value : new Less_Tree_Value(array($value));
+ $this->important = $important ? ' ' . trim($important) : '';
+ $this->merge = $merge;
+ $this->index = $index;
+ $this->currentFileInfo = $currentFileInfo;
+ $this->inline = $inline;
+ $this->variable = ( is_string($name) && $name[0] === '@');
+ }
+
+ public function accept($visitor) {
+ $this->value = $visitor->visitObj( $this->value );
+ }
+
+ /**
+ * @see Less_Tree::genCSS
+ */
+ public function genCSS( $output ){
+
+ $output->add( $this->name . Less_Environment::$_outputMap[': '], $this->currentFileInfo, $this->index);
+ try{
+ $this->value->genCSS( $output);
+
+ }catch( Less_Exception_Parser $e ){
+ $e->index = $this->index;
+ $e->currentFile = $this->currentFileInfo;
+ throw $e;
+ }
+ $output->add( $this->important . (($this->inline || (Less_Environment::$lastRule && Less_Parser::$options['compress'])) ? "" : ";"), $this->currentFileInfo, $this->index);
+ }
+
+ public function compile ($env){
+
+ $name = $this->name;
+ if( is_array($name) ){
+ // expand 'primitive' name directly to get
+ // things faster (~10% for benchmark.less):
+ if( count($name) === 1 && $name[0] instanceof Less_Tree_Keyword ){
+ $name = $name[0]->value;
+ }else{
+ $name = $this->CompileName($env,$name);
+ }
+ }
+
+ $strictMathBypass = Less_Parser::$options['strictMath'];
+ if( $name === "font" && !Less_Parser::$options['strictMath'] ){
+ Less_Parser::$options['strictMath'] = true;
+ }
+
+ try {
+ $evaldValue = $this->value->compile($env);
+
+ if( !$this->variable && $evaldValue->type === "DetachedRuleset") {
+ throw new Less_Exception_Compiler("Rulesets cannot be evaluated on a property.", null, $this->index, $this->currentFileInfo);
+ }
+
+ if( Less_Environment::$mixin_stack ){
+ $return = new Less_Tree_Rule($name, $evaldValue, $this->important, $this->merge, $this->index, $this->currentFileInfo, $this->inline);
+ }else{
+ $this->name = $name;
+ $this->value = $evaldValue;
+ $return = $this;
+ }
+
+ }catch( Less_Exception_Parser $e ){
+ if( !is_numeric($e->index) ){
+ $e->index = $this->index;
+ $e->currentFile = $this->currentFileInfo;
+ }
+ throw $e;
+ }
+
+ Less_Parser::$options['strictMath'] = $strictMathBypass;
+
+ return $return;
+ }
+
+
+ public function CompileName( $env, $name ){
+ $output = new Less_Output();
+ foreach($name as $n){
+ $n->compile($env)->genCSS($output);
+ }
+ return $output->toString();
+ }
+
+ public function makeImportant(){
+ return new Less_Tree_Rule($this->name, $this->value, '!important', $this->merge, $this->index, $this->currentFileInfo, $this->inline);
+ }
+
+}
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Ruleset.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Ruleset.php
new file mode 100644
index 000000000..bdf9fec79
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Ruleset.php
@@ -0,0 +1,643 @@
+ruleset_id = Less_Parser::$next_id++;
+ $this->originalRuleset = $this->ruleset_id;
+
+ if( $this->selectors ){
+ foreach($this->selectors as $sel){
+ if( $sel->_oelements ){
+ $this->first_oelements[$sel->_oelements[0]] = true;
+ }
+ }
+ }
+ }
+
+ public function __construct($selectors, $rules, $strictImports = null){
+ $this->selectors = $selectors;
+ $this->rules = $rules;
+ $this->lookups = array();
+ $this->strictImports = $strictImports;
+ $this->SetRulesetIndex();
+ }
+
+ public function accept( $visitor ){
+ if( $this->paths ){
+ $paths_len = count($this->paths);
+ for($i = 0,$paths_len; $i < $paths_len; $i++ ){
+ $this->paths[$i] = $visitor->visitArray($this->paths[$i]);
+ }
+ }elseif( $this->selectors ){
+ $this->selectors = $visitor->visitArray($this->selectors);
+ }
+
+ if( $this->rules ){
+ $this->rules = $visitor->visitArray($this->rules);
+ }
+ }
+
+ public function compile($env){
+
+ $ruleset = $this->PrepareRuleset($env);
+
+
+ // Store the frames around mixin definitions,
+ // so they can be evaluated like closures when the time comes.
+ $rsRuleCnt = count($ruleset->rules);
+ for( $i = 0; $i < $rsRuleCnt; $i++ ){
+ if( $ruleset->rules[$i] instanceof Less_Tree_Mixin_Definition || $ruleset->rules[$i] instanceof Less_Tree_DetachedRuleset ){
+ $ruleset->rules[$i] = $ruleset->rules[$i]->compile($env);
+ }
+ }
+
+ $mediaBlockCount = 0;
+ if( $env instanceof Less_Environment ){
+ $mediaBlockCount = count($env->mediaBlocks);
+ }
+
+ // Evaluate mixin calls.
+ $this->EvalMixinCalls( $ruleset, $env, $rsRuleCnt );
+
+
+ // Evaluate everything else
+ for( $i=0; $i<$rsRuleCnt; $i++ ){
+ if(! ($ruleset->rules[$i] instanceof Less_Tree_Mixin_Definition || $ruleset->rules[$i] instanceof Less_Tree_DetachedRuleset) ){
+ $ruleset->rules[$i] = $ruleset->rules[$i]->compile($env);
+ }
+ }
+
+ // Evaluate everything else
+ for( $i=0; $i<$rsRuleCnt; $i++ ){
+ $rule = $ruleset->rules[$i];
+
+ // for rulesets, check if it is a css guard and can be removed
+ if( $rule instanceof Less_Tree_Ruleset && $rule->selectors && count($rule->selectors) === 1 ){
+
+ // check if it can be folded in (e.g. & where)
+ if( $rule->selectors[0]->isJustParentSelector() ){
+ array_splice($ruleset->rules,$i--,1);
+ $rsRuleCnt--;
+
+ for($j = 0; $j < count($rule->rules); $j++ ){
+ $subRule = $rule->rules[$j];
+ if( !($subRule instanceof Less_Tree_Rule) || !$subRule->variable ){
+ array_splice($ruleset->rules, ++$i, 0, array($subRule));
+ $rsRuleCnt++;
+ }
+ }
+
+ }
+ }
+ }
+
+
+ // Pop the stack
+ $env->shiftFrame();
+
+ if ($mediaBlockCount) {
+ $len = count($env->mediaBlocks);
+ for($i = $mediaBlockCount; $i < $len; $i++ ){
+ $env->mediaBlocks[$i]->bubbleSelectors($ruleset->selectors);
+ }
+ }
+
+ return $ruleset;
+ }
+
+ /**
+ * Compile Less_Tree_Mixin_Call objects
+ *
+ * @param Less_Tree_Ruleset $ruleset
+ * @param integer $rsRuleCnt
+ */
+ private function EvalMixinCalls( $ruleset, $env, &$rsRuleCnt ){
+ for($i=0; $i < $rsRuleCnt; $i++){
+ $rule = $ruleset->rules[$i];
+
+ if( $rule instanceof Less_Tree_Mixin_Call ){
+ $rule = $rule->compile($env);
+
+ $temp = array();
+ foreach($rule as $r){
+ if( ($r instanceof Less_Tree_Rule) && $r->variable ){
+ // do not pollute the scope if the variable is
+ // already there. consider returning false here
+ // but we need a way to "return" variable from mixins
+ if( !$ruleset->variable($r->name) ){
+ $temp[] = $r;
+ }
+ }else{
+ $temp[] = $r;
+ }
+ }
+ $temp_count = count($temp)-1;
+ array_splice($ruleset->rules, $i, 1, $temp);
+ $rsRuleCnt += $temp_count;
+ $i += $temp_count;
+ $ruleset->resetCache();
+
+ }elseif( $rule instanceof Less_Tree_RulesetCall ){
+
+ $rule = $rule->compile($env);
+ $rules = array();
+ foreach($rule->rules as $r){
+ if( ($r instanceof Less_Tree_Rule) && $r->variable ){
+ continue;
+ }
+ $rules[] = $r;
+ }
+
+ array_splice($ruleset->rules, $i, 1, $rules);
+ $temp_count = count($rules);
+ $rsRuleCnt += $temp_count - 1;
+ $i += $temp_count-1;
+ $ruleset->resetCache();
+ }
+
+ }
+ }
+
+
+ /**
+ * Compile the selectors and create a new ruleset object for the compile() method
+ *
+ */
+ private function PrepareRuleset($env){
+
+ $hasOnePassingSelector = false;
+ $selectors = array();
+ if( $this->selectors ){
+ Less_Tree_DefaultFunc::error("it is currently only allowed in parametric mixin guards,");
+
+ foreach($this->selectors as $s){
+ $selector = $s->compile($env);
+ $selectors[] = $selector;
+ if( $selector->evaldCondition ){
+ $hasOnePassingSelector = true;
+ }
+ }
+
+ Less_Tree_DefaultFunc::reset();
+ } else {
+ $hasOnePassingSelector = true;
+ }
+
+ if( $this->rules && $hasOnePassingSelector ){
+ $rules = $this->rules;
+ }else{
+ $rules = array();
+ }
+
+ $ruleset = new Less_Tree_Ruleset($selectors, $rules, $this->strictImports);
+
+ $ruleset->originalRuleset = $this->ruleset_id;
+
+ $ruleset->root = $this->root;
+ $ruleset->firstRoot = $this->firstRoot;
+ $ruleset->allowImports = $this->allowImports;
+
+
+ // push the current ruleset to the frames stack
+ $env->unshiftFrame($ruleset);
+
+
+ // Evaluate imports
+ if( $ruleset->root || $ruleset->allowImports || !$ruleset->strictImports ){
+ $ruleset->evalImports($env);
+ }
+
+ return $ruleset;
+ }
+
+ function evalImports($env) {
+
+ $rules_len = count($this->rules);
+ for($i=0; $i < $rules_len; $i++){
+ $rule = $this->rules[$i];
+
+ if( $rule instanceof Less_Tree_Import ){
+ $rules = $rule->compile($env);
+ if( is_array($rules) ){
+ array_splice($this->rules, $i, 1, $rules);
+ $temp_count = count($rules)-1;
+ $i += $temp_count;
+ $rules_len += $temp_count;
+ }else{
+ array_splice($this->rules, $i, 1, array($rules));
+ }
+
+ $this->resetCache();
+ }
+ }
+ }
+
+ function makeImportant(){
+
+ $important_rules = array();
+ foreach($this->rules as $rule){
+ if( $rule instanceof Less_Tree_Rule || $rule instanceof Less_Tree_Ruleset || $rule instanceof Less_Tree_NameValue ){
+ $important_rules[] = $rule->makeImportant();
+ }else{
+ $important_rules[] = $rule;
+ }
+ }
+
+ return new Less_Tree_Ruleset($this->selectors, $important_rules, $this->strictImports );
+ }
+
+ public function matchArgs($args){
+ return !$args;
+ }
+
+ // lets you call a css selector with a guard
+ public function matchCondition( $args, $env ){
+ $lastSelector = end($this->selectors);
+
+ if( !$lastSelector->evaldCondition ){
+ return false;
+ }
+ if( $lastSelector->condition && !$lastSelector->condition->compile( $env->copyEvalEnv( $env->frames ) ) ){
+ return false;
+ }
+ return true;
+ }
+
+ function resetCache(){
+ $this->_rulesets = null;
+ $this->_variables = null;
+ $this->lookups = array();
+ }
+
+ public function variables(){
+ $this->_variables = array();
+ foreach( $this->rules as $r){
+ if ($r instanceof Less_Tree_Rule && $r->variable === true) {
+ $this->_variables[$r->name] = $r;
+ }
+ }
+ }
+
+ public function variable($name){
+
+ if( is_null($this->_variables) ){
+ $this->variables();
+ }
+ return isset($this->_variables[$name]) ? $this->_variables[$name] : null;
+ }
+
+ public function find( $selector, $self = null ){
+
+ $key = implode(' ',$selector->_oelements);
+
+ if( !isset($this->lookups[$key]) ){
+
+ if( !$self ){
+ $self = $this->ruleset_id;
+ }
+
+ $this->lookups[$key] = array();
+
+ $first_oelement = $selector->_oelements[0];
+
+ foreach($this->rules as $rule){
+ if( $rule instanceof Less_Tree_Ruleset && $rule->ruleset_id != $self ){
+
+ if( isset($rule->first_oelements[$first_oelement]) ){
+
+ foreach( $rule->selectors as $ruleSelector ){
+ $match = $selector->match($ruleSelector);
+ if( $match ){
+ if( $selector->elements_len > $match ){
+ $this->lookups[$key] = array_merge($this->lookups[$key], $rule->find( new Less_Tree_Selector(array_slice($selector->elements, $match)), $self));
+ } else {
+ $this->lookups[$key][] = $rule;
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return $this->lookups[$key];
+ }
+
+
+ /**
+ * @see Less_Tree::genCSS
+ */
+ public function genCSS( $output ){
+
+ if( !$this->root ){
+ Less_Environment::$tabLevel++;
+ }
+
+ $tabRuleStr = $tabSetStr = '';
+ if( !Less_Parser::$options['compress'] ){
+ if( Less_Environment::$tabLevel ){
+ $tabRuleStr = "\n".str_repeat( Less_Parser::$options['indentation'] , Less_Environment::$tabLevel );
+ $tabSetStr = "\n".str_repeat( Less_Parser::$options['indentation'] , Less_Environment::$tabLevel-1 );
+ }else{
+ $tabSetStr = $tabRuleStr = "\n";
+ }
+ }
+
+
+ $ruleNodes = array();
+ $rulesetNodes = array();
+ foreach($this->rules as $rule){
+
+ $class = get_class($rule);
+ if( ($class === 'Less_Tree_Media') || ($class === 'Less_Tree_Directive') || ($this->root && $class === 'Less_Tree_Comment') || ($class === 'Less_Tree_Ruleset' && $rule->rules) ){
+ $rulesetNodes[] = $rule;
+ }else{
+ $ruleNodes[] = $rule;
+ }
+ }
+
+ // If this is the root node, we don't render
+ // a selector, or {}.
+ if( !$this->root ){
+
+ /*
+ debugInfo = tree.debugInfo(env, this, tabSetStr);
+
+ if (debugInfo) {
+ output.add(debugInfo);
+ output.add(tabSetStr);
+ }
+ */
+
+ $paths_len = count($this->paths);
+ for( $i = 0; $i < $paths_len; $i++ ){
+ $path = $this->paths[$i];
+ $firstSelector = true;
+
+ foreach($path as $p){
+ $p->genCSS( $output, $firstSelector );
+ $firstSelector = false;
+ }
+
+ if( $i + 1 < $paths_len ){
+ $output->add( ',' . $tabSetStr );
+ }
+ }
+
+ $output->add( (Less_Parser::$options['compress'] ? '{' : " {") . $tabRuleStr );
+ }
+
+ // Compile rules and rulesets
+ $ruleNodes_len = count($ruleNodes);
+ $rulesetNodes_len = count($rulesetNodes);
+ for( $i = 0; $i < $ruleNodes_len; $i++ ){
+ $rule = $ruleNodes[$i];
+
+ // @page{ directive ends up with root elements inside it, a mix of rules and rulesets
+ // In this instance we do not know whether it is the last property
+ if( $i + 1 === $ruleNodes_len && (!$this->root || $rulesetNodes_len === 0 || $this->firstRoot ) ){
+ Less_Environment::$lastRule = true;
+ }
+
+ $rule->genCSS( $output );
+
+ if( !Less_Environment::$lastRule ){
+ $output->add( $tabRuleStr );
+ }else{
+ Less_Environment::$lastRule = false;
+ }
+ }
+
+ if( !$this->root ){
+ $output->add( $tabSetStr . '}' );
+ Less_Environment::$tabLevel--;
+ }
+
+ $firstRuleset = true;
+ $space = ($this->root ? $tabRuleStr : $tabSetStr);
+ for( $i = 0; $i < $rulesetNodes_len; $i++ ){
+
+ if( $ruleNodes_len && $firstRuleset ){
+ $output->add( $space );
+ }elseif( !$firstRuleset ){
+ $output->add( $space );
+ }
+ $firstRuleset = false;
+ $rulesetNodes[$i]->genCSS( $output);
+ }
+
+ if( !Less_Parser::$options['compress'] && $this->firstRoot ){
+ $output->add( "\n" );
+ }
+
+ }
+
+
+ function markReferenced(){
+ if( !$this->selectors ){
+ return;
+ }
+ foreach($this->selectors as $selector){
+ $selector->markReferenced();
+ }
+ }
+
+ public function joinSelectors( $context, $selectors ){
+ $paths = array();
+ if( is_array($selectors) ){
+ foreach($selectors as $selector) {
+ $this->joinSelector( $paths, $context, $selector);
+ }
+ }
+ return $paths;
+ }
+
+ public function joinSelector( &$paths, $context, $selector){
+
+ $hasParentSelector = false;
+
+ foreach($selector->elements as $el) {
+ if( $el->value === '&') {
+ $hasParentSelector = true;
+ }
+ }
+
+ if( !$hasParentSelector ){
+ if( $context ){
+ foreach($context as $context_el){
+ $paths[] = array_merge($context_el, array($selector) );
+ }
+ }else {
+ $paths[] = array($selector);
+ }
+ return;
+ }
+
+
+ // The paths are [[Selector]]
+ // The first list is a list of comma separated selectors
+ // The inner list is a list of inheritance separated selectors
+ // e.g.
+ // .a, .b {
+ // .c {
+ // }
+ // }
+ // == [[.a] [.c]] [[.b] [.c]]
+ //
+
+ // the elements from the current selector so far
+ $currentElements = array();
+ // the current list of new selectors to add to the path.
+ // We will build it up. We initiate it with one empty selector as we "multiply" the new selectors
+ // by the parents
+ $newSelectors = array(array());
+
+
+ foreach( $selector->elements as $el){
+
+ // non parent reference elements just get added
+ if( $el->value !== '&' ){
+ $currentElements[] = $el;
+ } else {
+ // the new list of selectors to add
+ $selectorsMultiplied = array();
+
+ // merge the current list of non parent selector elements
+ // on to the current list of selectors to add
+ if( $currentElements ){
+ $this->mergeElementsOnToSelectors( $currentElements, $newSelectors);
+ }
+
+ // loop through our current selectors
+ foreach($newSelectors as $sel){
+
+ // if we don't have any parent paths, the & might be in a mixin so that it can be used
+ // whether there are parents or not
+ if( !$context ){
+ // the combinator used on el should now be applied to the next element instead so that
+ // it is not lost
+ if( $sel ){
+ $sel[0]->elements = array_slice($sel[0]->elements,0);
+ $sel[0]->elements[] = new Less_Tree_Element($el->combinator, '', $el->index, $el->currentFileInfo );
+ }
+ $selectorsMultiplied[] = $sel;
+ }else {
+
+ // and the parent selectors
+ foreach($context as $parentSel){
+ // We need to put the current selectors
+ // then join the last selector's elements on to the parents selectors
+
+ // our new selector path
+ $newSelectorPath = array();
+ // selectors from the parent after the join
+ $afterParentJoin = array();
+ $newJoinedSelectorEmpty = true;
+
+ //construct the joined selector - if & is the first thing this will be empty,
+ // if not newJoinedSelector will be the last set of elements in the selector
+ if( $sel ){
+ $newSelectorPath = $sel;
+ $lastSelector = array_pop($newSelectorPath);
+ $newJoinedSelector = $selector->createDerived( array_slice($lastSelector->elements,0) );
+ $newJoinedSelectorEmpty = false;
+ }
+ else {
+ $newJoinedSelector = $selector->createDerived(array());
+ }
+
+ //put together the parent selectors after the join
+ if ( count($parentSel) > 1) {
+ $afterParentJoin = array_merge($afterParentJoin, array_slice($parentSel,1) );
+ }
+
+ if ( $parentSel ){
+ $newJoinedSelectorEmpty = false;
+
+ // join the elements so far with the first part of the parent
+ $newJoinedSelector->elements[] = new Less_Tree_Element( $el->combinator, $parentSel[0]->elements[0]->value, $el->index, $el->currentFileInfo);
+
+ $newJoinedSelector->elements = array_merge( $newJoinedSelector->elements, array_slice($parentSel[0]->elements, 1) );
+ }
+
+ if (!$newJoinedSelectorEmpty) {
+ // now add the joined selector
+ $newSelectorPath[] = $newJoinedSelector;
+ }
+
+ // and the rest of the parent
+ $newSelectorPath = array_merge($newSelectorPath, $afterParentJoin);
+
+ // add that to our new set of selectors
+ $selectorsMultiplied[] = $newSelectorPath;
+ }
+ }
+ }
+
+ // our new selectors has been multiplied, so reset the state
+ $newSelectors = $selectorsMultiplied;
+ $currentElements = array();
+ }
+ }
+
+ // if we have any elements left over (e.g. .a& .b == .b)
+ // add them on to all the current selectors
+ if( $currentElements ){
+ $this->mergeElementsOnToSelectors($currentElements, $newSelectors);
+ }
+ foreach( $newSelectors as $new_sel){
+ if( $new_sel ){
+ $paths[] = $new_sel;
+ }
+ }
+ }
+
+ function mergeElementsOnToSelectors( $elements, &$selectors){
+
+ if( !$selectors ){
+ $selectors[] = array( new Less_Tree_Selector($elements) );
+ return;
+ }
+
+
+ foreach( $selectors as &$sel){
+
+ // if the previous thing in sel is a parent this needs to join on to it
+ if( $sel ){
+ $last = count($sel)-1;
+ $sel[$last] = $sel[$last]->createDerived( array_merge($sel[$last]->elements, $elements) );
+ }else{
+ $sel[] = new Less_Tree_Selector( $elements );
+ }
+ }
+ }
+}
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/RulesetCall.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/RulesetCall.php
new file mode 100644
index 000000000..ed4c72373
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/RulesetCall.php
@@ -0,0 +1,26 @@
+variable = $variable;
+ }
+
+ public function accept($visitor) {}
+
+ public function compile( $env ){
+ $variable = new Less_Tree_Variable($this->variable);
+ $detachedRuleset = $variable->compile($env);
+ return $detachedRuleset->callEval($env);
+ }
+}
+
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Selector.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Selector.php
new file mode 100644
index 000000000..6b9dae612
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Selector.php
@@ -0,0 +1,168 @@
+elements = $elements;
+ $this->elements_len = count($elements);
+ $this->extendList = $extendList;
+ $this->condition = $condition;
+ if( $currentFileInfo ){
+ $this->currentFileInfo = $currentFileInfo;
+ }
+ $this->isReferenced = $isReferenced;
+ if( !$condition ){
+ $this->evaldCondition = true;
+ }
+
+ $this->CacheElements();
+ }
+
+ public function accept($visitor) {
+ $this->elements = $visitor->visitArray($this->elements);
+ $this->extendList = $visitor->visitArray($this->extendList);
+ if( $this->condition ){
+ $this->condition = $visitor->visitObj($this->condition);
+ }
+
+ if( $visitor instanceof Less_Visitor_extendFinder ){
+ $this->CacheElements();
+ }
+ }
+
+ public function createDerived( $elements, $extendList = null, $evaldCondition = null ){
+ $newSelector = new Less_Tree_Selector( $elements, ($extendList ? $extendList : $this->extendList), null, $this->index, $this->currentFileInfo, $this->isReferenced);
+ $newSelector->evaldCondition = $evaldCondition ? $evaldCondition : $this->evaldCondition;
+ return $newSelector;
+ }
+
+
+ public function match( $other ){
+
+ if( !$other->_oelements || ($this->elements_len < $other->_oelements_len) ){
+ return 0;
+ }
+
+ for( $i = 0; $i < $other->_oelements_len; $i++ ){
+ if( $this->elements[$i]->value !== $other->_oelements[$i]) {
+ return 0;
+ }
+ }
+
+ return $other->_oelements_len; // return number of matched elements
+ }
+
+
+ public function CacheElements(){
+
+ $this->_oelements = array();
+ $css = '';
+
+ foreach($this->elements as $v){
+
+ $css .= $v->combinator;
+ if( !$v->value_is_object ){
+ $css .= $v->value;
+ continue;
+ }
+
+ if( !property_exists($v->value,'value') || !is_string($v->value->value) ){
+ $this->cacheable = false;
+ return;
+ }
+ $css .= $v->value->value;
+ }
+
+ $this->_oelements_len = preg_match_all('/[,\.\w-](?:[\w-]|(?:\\\\.))*/', $css, $matches);
+ if( $this->_oelements_len ){
+ $this->_oelements = $matches[0];
+
+ if( $this->_oelements[0] === '&' ){
+ array_shift($this->_oelements);
+ $this->_oelements_len--;
+ }
+ }
+ }
+
+ public function isJustParentSelector(){
+ return !$this->mediaEmpty &&
+ count($this->elements) === 1 &&
+ $this->elements[0]->value === '&' &&
+ ($this->elements[0]->combinator === ' ' || $this->elements[0]->combinator === '');
+ }
+
+ public function compile($env) {
+
+ $elements = array();
+ foreach($this->elements as $el){
+ $elements[] = $el->compile($env);
+ }
+
+ $extendList = array();
+ foreach($this->extendList as $el){
+ $extendList[] = $el->compile($el);
+ }
+
+ $evaldCondition = false;
+ if( $this->condition ){
+ $evaldCondition = $this->condition->compile($env);
+ }
+
+ return $this->createDerived( $elements, $extendList, $evaldCondition );
+ }
+
+
+ /**
+ * @see Less_Tree::genCSS
+ */
+ public function genCSS( $output, $firstSelector = true ){
+
+ if( !$firstSelector && $this->elements[0]->combinator === "" ){
+ $output->add(' ', $this->currentFileInfo, $this->index);
+ }
+
+ foreach($this->elements as $element){
+ $element->genCSS( $output );
+ }
+ }
+
+ public function markReferenced(){
+ $this->isReferenced = true;
+ }
+
+ public function getIsReferenced(){
+ return !isset($this->currentFileInfo['reference']) || !$this->currentFileInfo['reference'] || $this->isReferenced;
+ }
+
+ public function getIsOutput(){
+ return $this->evaldCondition;
+ }
+
+}
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/UnicodeDescriptor.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/UnicodeDescriptor.php
new file mode 100644
index 000000000..8c4707efd
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/UnicodeDescriptor.php
@@ -0,0 +1,29 @@
+value = $value;
+ }
+
+ /**
+ * @see Less_Tree::genCSS
+ */
+ public function genCSS( $output ){
+ $output->add( $this->value );
+ }
+
+ public function compile(){
+ return $this;
+ }
+}
+
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Unit.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Unit.php
new file mode 100644
index 000000000..e13b100e2
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Unit.php
@@ -0,0 +1,147 @@
+numerator = $numerator;
+ $this->denominator = $denominator;
+ $this->backupUnit = $backupUnit;
+ }
+
+ public function __clone(){
+ }
+
+ /**
+ * @see Less_Tree::genCSS
+ */
+ public function genCSS( $output ){
+
+ if( $this->numerator ){
+ $output->add( $this->numerator[0] );
+ }elseif( $this->denominator ){
+ $output->add( $this->denominator[0] );
+ }elseif( !Less_Parser::$options['strictUnits'] && $this->backupUnit ){
+ $output->add( $this->backupUnit );
+ return ;
+ }
+ }
+
+ public function toString(){
+ $returnStr = implode('*',$this->numerator);
+ foreach($this->denominator as $d){
+ $returnStr .= '/'.$d;
+ }
+ return $returnStr;
+ }
+
+ public function __toString(){
+ return $this->toString();
+ }
+
+
+ /**
+ * @param Less_Tree_Unit $other
+ */
+ public function compare($other) {
+ return $this->is( $other->toString() ) ? 0 : -1;
+ }
+
+ public function is($unitString){
+ return $this->toString() === $unitString;
+ }
+
+ public function isLength(){
+ $css = $this->toCSS();
+ return !!preg_match('/px|em|%|in|cm|mm|pc|pt|ex/',$css);
+ }
+
+ public function isAngle() {
+ return isset( Less_Tree_UnitConversions::$angle[$this->toCSS()] );
+ }
+
+ public function isEmpty(){
+ return !$this->numerator && !$this->denominator;
+ }
+
+ public function isSingular() {
+ return count($this->numerator) <= 1 && !$this->denominator;
+ }
+
+
+ public function usedUnits(){
+ $result = array();
+
+ foreach(Less_Tree_UnitConversions::$groups as $groupName){
+ $group = Less_Tree_UnitConversions::${$groupName};
+
+ foreach($this->numerator as $atomicUnit){
+ if( isset($group[$atomicUnit]) && !isset($result[$groupName]) ){
+ $result[$groupName] = $atomicUnit;
+ }
+ }
+
+ foreach($this->denominator as $atomicUnit){
+ if( isset($group[$atomicUnit]) && !isset($result[$groupName]) ){
+ $result[$groupName] = $atomicUnit;
+ }
+ }
+ }
+
+ return $result;
+ }
+
+ public function cancel(){
+ $counter = array();
+ $backup = null;
+
+ foreach($this->numerator as $atomicUnit){
+ if( !$backup ){
+ $backup = $atomicUnit;
+ }
+ $counter[$atomicUnit] = ( isset($counter[$atomicUnit]) ? $counter[$atomicUnit] : 0) + 1;
+ }
+
+ foreach($this->denominator as $atomicUnit){
+ if( !$backup ){
+ $backup = $atomicUnit;
+ }
+ $counter[$atomicUnit] = ( isset($counter[$atomicUnit]) ? $counter[$atomicUnit] : 0) - 1;
+ }
+
+ $this->numerator = array();
+ $this->denominator = array();
+
+ foreach($counter as $atomicUnit => $count){
+ if( $count > 0 ){
+ for( $i = 0; $i < $count; $i++ ){
+ $this->numerator[] = $atomicUnit;
+ }
+ }elseif( $count < 0 ){
+ for( $i = 0; $i < -$count; $i++ ){
+ $this->denominator[] = $atomicUnit;
+ }
+ }
+ }
+
+ if( !$this->numerator && !$this->denominator && $backup ){
+ $this->backupUnit = $backup;
+ }
+
+ sort($this->numerator);
+ sort($this->denominator);
+ }
+
+
+}
+
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/UnitConversions.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/UnitConversions.php
new file mode 100644
index 000000000..c86b2907b
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/UnitConversions.php
@@ -0,0 +1,35 @@
+ 1,
+ 'cm'=> 0.01,
+ 'mm'=> 0.001,
+ 'in'=> 0.0254,
+ 'px'=> 0.000264583, // 0.0254 / 96,
+ 'pt'=> 0.000352778, // 0.0254 / 72,
+ 'pc'=> 0.004233333, // 0.0254 / 72 * 12
+ );
+
+ public static $duration = array(
+ 's'=> 1,
+ 'ms'=> 0.001
+ );
+
+ public static $angle = array(
+ 'rad' => 0.1591549430919, // 1/(2*M_PI),
+ 'deg' => 0.002777778, // 1/360,
+ 'grad'=> 0.0025, // 1/400,
+ 'turn'=> 1
+ );
+
+}
\ No newline at end of file
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Url.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Url.php
new file mode 100644
index 000000000..ef9c3c68c
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Url.php
@@ -0,0 +1,76 @@
+value = $value;
+ $this->currentFileInfo = $currentFileInfo;
+ $this->isEvald = $isEvald;
+ }
+
+ public function accept( $visitor ){
+ $this->value = $visitor->visitObj($this->value);
+ }
+
+ /**
+ * @see Less_Tree::genCSS
+ */
+ public function genCSS( $output ){
+ $output->add( 'url(' );
+ $this->value->genCSS( $output );
+ $output->add( ')' );
+ }
+
+ /**
+ * @param Less_Functions $ctx
+ */
+ public function compile($ctx){
+ $val = $this->value->compile($ctx);
+
+ if( !$this->isEvald ){
+ // Add the base path if the URL is relative
+ if( Less_Parser::$options['relativeUrls']
+ && $this->currentFileInfo
+ && is_string($val->value)
+ && Less_Environment::isPathRelative($val->value)
+ ){
+ $rootpath = $this->currentFileInfo['uri_root'];
+ if ( !$val->quote ){
+ $rootpath = preg_replace('/[\(\)\'"\s]/', '\\$1', $rootpath );
+ }
+ $val->value = $rootpath . $val->value;
+ }
+
+ $val->value = Less_Environment::normalizePath( $val->value);
+ }
+
+ // Add cache buster if enabled
+ if( Less_Parser::$options['urlArgs'] ){
+ if( !preg_match('/^\s*data:/',$val->value) ){
+ $delimiter = strpos($val->value,'?') === false ? '?' : '&';
+ $urlArgs = $delimiter . Less_Parser::$options['urlArgs'];
+ $hash_pos = strpos($val->value,'#');
+ if( $hash_pos !== false ){
+ $val->value = substr_replace($val->value,$urlArgs, $hash_pos, 0);
+ } else {
+ $val->value .= $urlArgs;
+ }
+ }
+ }
+
+ return new Less_Tree_URL($val, $this->currentFileInfo, true);
+ }
+
+}
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Value.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Value.php
new file mode 100644
index 000000000..9f077bc56
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Value.php
@@ -0,0 +1,48 @@
+value = $value;
+ }
+
+ public function accept($visitor) {
+ $this->value = $visitor->visitArray($this->value);
+ }
+
+ public function compile($env){
+
+ $ret = array();
+ $i = 0;
+ foreach($this->value as $i => $v){
+ $ret[] = $v->compile($env);
+ }
+ if( $i > 0 ){
+ return new Less_Tree_Value($ret);
+ }
+ return $ret[0];
+ }
+
+ /**
+ * @see Less_Tree::genCSS
+ */
+ function genCSS( $output ){
+ $len = count($this->value);
+ for($i = 0; $i < $len; $i++ ){
+ $this->value[$i]->genCSS( $output );
+ if( $i+1 < $len ){
+ $output->add( Less_Environment::$_outputMap[','] );
+ }
+ }
+ }
+
+}
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Variable.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Variable.php
new file mode 100644
index 000000000..cc0182cde
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Tree/Variable.php
@@ -0,0 +1,52 @@
+name = $name;
+ $this->index = $index;
+ $this->currentFileInfo = $currentFileInfo;
+ }
+
+ public function compile($env) {
+
+ if( $this->name[1] === '@' ){
+ $v = new Less_Tree_Variable(substr($this->name, 1), $this->index + 1, $this->currentFileInfo);
+ $name = '@' . $v->compile($env)->value;
+ }else{
+ $name = $this->name;
+ }
+
+ if ($this->evaluating) {
+ throw new Less_Exception_Compiler("Recursive variable definition for " . $name, null, $this->index, $this->currentFileInfo);
+ }
+
+ $this->evaluating = true;
+
+ foreach($env->frames as $frame){
+ if( $v = $frame->variable($name) ){
+ $r = $v->value->compile($env);
+ $this->evaluating = false;
+ return $r;
+ }
+ }
+
+ throw new Less_Exception_Compiler("variable " . $name . " is undefined in file ".$this->currentFileInfo["filename"], null, $this->index, $this->currentFileInfo);
+ }
+
+}
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Version.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Version.php
new file mode 100644
index 000000000..dceefe5d3
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Version.php
@@ -0,0 +1,15 @@
+_visitFnCache = get_class_methods(get_class($this));
+ $this->_visitFnCache = array_flip($this->_visitFnCache);
+ }
+
+ public function visitObj( $node ){
+
+ $funcName = 'visit'.$node->type;
+ if( isset($this->_visitFnCache[$funcName]) ){
+
+ $visitDeeper = true;
+ $this->$funcName( $node, $visitDeeper );
+
+ if( $visitDeeper ){
+ $node->accept($this);
+ }
+
+ $funcName = $funcName . "Out";
+ if( isset($this->_visitFnCache[$funcName]) ){
+ $this->$funcName( $node );
+ }
+
+ }else{
+ $node->accept($this);
+ }
+
+ return $node;
+ }
+
+ public function visitArray( $nodes ){
+
+ array_map( array($this,'visitObj'), $nodes);
+ return $nodes;
+ }
+}
+
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Visitor/extendFinder.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Visitor/extendFinder.php
new file mode 100644
index 000000000..22b3aac99
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Visitor/extendFinder.php
@@ -0,0 +1,114 @@
+contexts = array();
+ $this->allExtendsStack = array(array());
+ parent::__construct();
+ }
+
+ /**
+ * @param Less_Tree_Ruleset $root
+ */
+ public function run($root){
+ $root = $this->visitObj($root);
+ $root->allExtends =& $this->allExtendsStack[0];
+ return $root;
+ }
+
+ public function visitRule($ruleNode, &$visitDeeper ){
+ $visitDeeper = false;
+ }
+
+ public function visitMixinDefinition( $mixinDefinitionNode, &$visitDeeper ){
+ $visitDeeper = false;
+ }
+
+ public function visitRuleset($rulesetNode){
+
+ if( $rulesetNode->root ){
+ return;
+ }
+
+ $allSelectorsExtendList = array();
+
+ // get &:extend(.a); rules which apply to all selectors in this ruleset
+ if( $rulesetNode->rules ){
+ foreach($rulesetNode->rules as $rule){
+ if( $rule instanceof Less_Tree_Extend ){
+ $allSelectorsExtendList[] = $rule;
+ $rulesetNode->extendOnEveryPath = true;
+ }
+ }
+ }
+
+
+ // now find every selector and apply the extends that apply to all extends
+ // and the ones which apply to an individual extend
+ foreach($rulesetNode->paths as $selectorPath){
+ $selector = end($selectorPath); //$selectorPath[ count($selectorPath)-1];
+
+ $j = 0;
+ foreach($selector->extendList as $extend){
+ $this->allExtendsStackPush($rulesetNode, $selectorPath, $extend, $j);
+ }
+ foreach($allSelectorsExtendList as $extend){
+ $this->allExtendsStackPush($rulesetNode, $selectorPath, $extend, $j);
+ }
+ }
+
+ $this->contexts[] = $rulesetNode->selectors;
+ }
+
+ public function allExtendsStackPush($rulesetNode, $selectorPath, $extend, &$j){
+ $this->foundExtends = true;
+ $extend = clone $extend;
+ $extend->findSelfSelectors( $selectorPath );
+ $extend->ruleset = $rulesetNode;
+ if( $j === 0 ){
+ $extend->firstExtendOnThisSelectorPath = true;
+ }
+
+ $end_key = count($this->allExtendsStack)-1;
+ $this->allExtendsStack[$end_key][] = $extend;
+ $j++;
+ }
+
+
+ public function visitRulesetOut( $rulesetNode ){
+ if( !is_object($rulesetNode) || !$rulesetNode->root ){
+ array_pop($this->contexts);
+ }
+ }
+
+ public function visitMedia( $mediaNode ){
+ $mediaNode->allExtends = array();
+ $this->allExtendsStack[] =& $mediaNode->allExtends;
+ }
+
+ public function visitMediaOut(){
+ array_pop($this->allExtendsStack);
+ }
+
+ public function visitDirective( $directiveNode ){
+ $directiveNode->allExtends = array();
+ $this->allExtendsStack[] =& $directiveNode->allExtends;
+ }
+
+ public function visitDirectiveOut(){
+ array_pop($this->allExtendsStack);
+ }
+}
+
+
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Visitor/import.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Visitor/import.php
new file mode 100644
index 000000000..f79a36dac
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Visitor/import.php
@@ -0,0 +1,139 @@
+env = $evalEnv;
+ $this->importCount = 0;
+ parent::__construct();
+ }
+
+
+ function run( $root ){
+ $root = $this->visitObj($root);
+ $this->isFinished = true;
+
+ //if( $this->importCount === 0) {
+ // $this->_finish();
+ //}
+ }
+
+ function visitImport($importNode, &$visitDeeper ){
+ $importVisitor = $this;
+ $inlineCSS = $importNode->options['inline'];
+
+ if( !$importNode->css || $inlineCSS ){
+ $evaldImportNode = $importNode->compileForImport($this->env);
+
+ if( $evaldImportNode && (!$evaldImportNode->css || $inlineCSS) ){
+ $importNode = $evaldImportNode;
+ $this->importCount++;
+ $env = clone $this->env;
+
+ if( (isset($importNode->options['multiple']) && $importNode->options['multiple']) ){
+ $env->importMultiple = true;
+ }
+
+ //get path & uri
+ $path_and_uri = null;
+ if( is_callable(Less_Parser::$options['import_callback']) ){
+ $path_and_uri = call_user_func(Less_Parser::$options['import_callback'],$importNode);
+ }
+
+ if( !$path_and_uri ){
+ $path_and_uri = $importNode->PathAndUri();
+ }
+
+ if( $path_and_uri ){
+ list($full_path, $uri) = $path_and_uri;
+ }else{
+ $full_path = $uri = $importNode->getPath();
+ }
+
+
+ //import once
+ if( $importNode->skip( $full_path, $env) ){
+ return array();
+ }
+
+ if( $importNode->options['inline'] ){
+ //todo needs to reference css file not import
+ //$contents = new Less_Tree_Anonymous($importNode->root, 0, array('filename'=>$importNode->importedFilename), true );
+
+ Less_Parser::AddParsedFile($full_path);
+ $contents = new Less_Tree_Anonymous( file_get_contents($full_path), 0, array(), true );
+
+ if( $importNode->features ){
+ return new Less_Tree_Media( array($contents), $importNode->features->value );
+ }
+
+ return array( $contents );
+ }
+
+
+ // css ?
+ if( $importNode->css ){
+ $features = ( $importNode->features ? $importNode->features->compile($env) : null );
+ return new Less_Tree_Import( $importNode->compilePath( $env), $features, $importNode->options, $this->index);
+ }
+
+ return $importNode->ParseImport( $full_path, $uri, $env );
+ }
+
+ }
+
+ $visitDeeper = false;
+ return $importNode;
+ }
+
+
+ function visitRule( $ruleNode, &$visitDeeper ){
+ $visitDeeper = false;
+ return $ruleNode;
+ }
+
+ function visitDirective($directiveNode, $visitArgs){
+ array_unshift($this->env->frames,$directiveNode);
+ return $directiveNode;
+ }
+
+ function visitDirectiveOut($directiveNode) {
+ array_shift($this->env->frames);
+ }
+
+ function visitMixinDefinition($mixinDefinitionNode, $visitArgs) {
+ array_unshift($this->env->frames,$mixinDefinitionNode);
+ return $mixinDefinitionNode;
+ }
+
+ function visitMixinDefinitionOut($mixinDefinitionNode) {
+ array_shift($this->env->frames);
+ }
+
+ function visitRuleset($rulesetNode, $visitArgs) {
+ array_unshift($this->env->frames,$rulesetNode);
+ return $rulesetNode;
+ }
+
+ function visitRulesetOut($rulesetNode) {
+ array_shift($this->env->frames);
+ }
+
+ function visitMedia($mediaNode, $visitArgs) {
+ array_unshift($this->env->frames, $mediaNode->ruleset);
+ return $mediaNode;
+ }
+
+ function visitMediaOut($mediaNode) {
+ array_shift($this->env->frames);
+ }
+
+}
+*/
+
+
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Visitor/joinSelector.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Visitor/joinSelector.php
new file mode 100644
index 000000000..f62af1a98
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Visitor/joinSelector.php
@@ -0,0 +1,70 @@
+visitObj($root);
+ }
+
+ public function visitRule( $ruleNode, &$visitDeeper ){
+ $visitDeeper = false;
+ }
+
+ public function visitMixinDefinition( $mixinDefinitionNode, &$visitDeeper ){
+ $visitDeeper = false;
+ }
+
+ public function visitRuleset( $rulesetNode ){
+
+ $paths = array();
+
+ if( !$rulesetNode->root ){
+ $selectors = array();
+
+ if( $rulesetNode->selectors && $rulesetNode->selectors ){
+ foreach($rulesetNode->selectors as $selector){
+ if( $selector->getIsOutput() ){
+ $selectors[] = $selector;
+ }
+ }
+ }
+
+ if( !$selectors ){
+ $rulesetNode->selectors = null;
+ $rulesetNode->rules = null;
+ }else{
+ $context = end($this->contexts); //$context = $this->contexts[ count($this->contexts) - 1];
+ $paths = $rulesetNode->joinSelectors( $context, $selectors);
+ }
+
+ $rulesetNode->paths = $paths;
+ }
+
+ $this->contexts[] = $paths; //different from less.js. Placed after joinSelectors() so that $this->contexts will get correct $paths
+ }
+
+ public function visitRulesetOut(){
+ array_pop($this->contexts);
+ }
+
+ public function visitMedia($mediaNode) {
+ $context = end($this->contexts); //$context = $this->contexts[ count($this->contexts) - 1];
+
+ if( !count($context) || (is_object($context[0]) && $context[0]->multiMedia) ){
+ $mediaNode->rules[0]->root = true;
+ }
+ }
+
+}
+
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Visitor/processExtends.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Visitor/processExtends.php
new file mode 100644
index 000000000..bb5f08246
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Visitor/processExtends.php
@@ -0,0 +1,469 @@
+run( $root );
+ if( !$extendFinder->foundExtends){
+ return $root;
+ }
+
+ $root->allExtends = $this->doExtendChaining( $root->allExtends, $root->allExtends);
+
+ $this->allExtendsStack = array();
+ $this->allExtendsStack[] = &$root->allExtends;
+
+ return $this->visitObj( $root );
+ }
+
+ private function doExtendChaining( $extendsList, $extendsListTarget, $iterationCount = 0){
+ //
+ // chaining is different from normal extension.. if we extend an extend then we are not just copying, altering and pasting
+ // the selector we would do normally, but we are also adding an extend with the same target selector
+ // this means this new extend can then go and alter other extends
+ //
+ // this method deals with all the chaining work - without it, extend is flat and doesn't work on other extend selectors
+ // this is also the most expensive.. and a match on one selector can cause an extension of a selector we had already processed if
+ // we look at each selector at a time, as is done in visitRuleset
+
+ $extendsToAdd = array();
+
+
+ //loop through comparing every extend with every target extend.
+ // a target extend is the one on the ruleset we are looking at copy/edit/pasting in place
+ // e.g. .a:extend(.b) {} and .b:extend(.c) {} then the first extend extends the second one
+ // and the second is the target.
+ // the separation into two lists allows us to process a subset of chains with a bigger set, as is the
+ // case when processing media queries
+ for( $extendIndex = 0, $extendsList_len = count($extendsList); $extendIndex < $extendsList_len; $extendIndex++ ){
+ for( $targetExtendIndex = 0; $targetExtendIndex < count($extendsListTarget); $targetExtendIndex++ ){
+
+ $extend = $extendsList[$extendIndex];
+ $targetExtend = $extendsListTarget[$targetExtendIndex];
+
+ // look for circular references
+ if( in_array($targetExtend->object_id, $extend->parent_ids,true) ){
+ continue;
+ }
+
+ // find a match in the target extends self selector (the bit before :extend)
+ $selectorPath = array( $targetExtend->selfSelectors[0] );
+ $matches = $this->findMatch( $extend, $selectorPath);
+
+
+ if( $matches ){
+
+ // we found a match, so for each self selector..
+ foreach($extend->selfSelectors as $selfSelector ){
+
+
+ // process the extend as usual
+ $newSelector = $this->extendSelector( $matches, $selectorPath, $selfSelector);
+
+ // but now we create a new extend from it
+ $newExtend = new Less_Tree_Extend( $targetExtend->selector, $targetExtend->option, 0);
+ $newExtend->selfSelectors = $newSelector;
+
+ // add the extend onto the list of extends for that selector
+ end($newSelector)->extendList = array($newExtend);
+ //$newSelector[ count($newSelector)-1]->extendList = array($newExtend);
+
+ // record that we need to add it.
+ $extendsToAdd[] = $newExtend;
+ $newExtend->ruleset = $targetExtend->ruleset;
+
+ //remember its parents for circular references
+ $newExtend->parent_ids = array_merge($newExtend->parent_ids,$targetExtend->parent_ids,$extend->parent_ids);
+
+ // only process the selector once.. if we have :extend(.a,.b) then multiple
+ // extends will look at the same selector path, so when extending
+ // we know that any others will be duplicates in terms of what is added to the css
+ if( $targetExtend->firstExtendOnThisSelectorPath ){
+ $newExtend->firstExtendOnThisSelectorPath = true;
+ $targetExtend->ruleset->paths[] = $newSelector;
+ }
+ }
+ }
+ }
+ }
+
+ if( $extendsToAdd ){
+ // try to detect circular references to stop a stack overflow.
+ // may no longer be needed. $this->extendChainCount++;
+ if( $iterationCount > 100) {
+
+ try{
+ $selectorOne = $extendsToAdd[0]->selfSelectors[0]->toCSS();
+ $selectorTwo = $extendsToAdd[0]->selector->toCSS();
+ }catch(Exception $e){
+ $selectorOne = "{unable to calculate}";
+ $selectorTwo = "{unable to calculate}";
+ }
+
+ throw new Less_Exception_Parser("extend circular reference detected. One of the circular extends is currently:" . $selectorOne . ":extend(" . $selectorTwo . ")");
+ }
+
+ // now process the new extends on the existing rules so that we can handle a extending b extending c ectending d extending e...
+ $extendsToAdd = $this->doExtendChaining( $extendsToAdd, $extendsListTarget, $iterationCount+1);
+ }
+
+ return array_merge($extendsList, $extendsToAdd);
+ }
+
+
+ protected function visitRule( $ruleNode, &$visitDeeper ){
+ $visitDeeper = false;
+ }
+
+ protected function visitMixinDefinition( $mixinDefinitionNode, &$visitDeeper ){
+ $visitDeeper = false;
+ }
+
+ protected function visitSelector( $selectorNode, &$visitDeeper ){
+ $visitDeeper = false;
+ }
+
+ protected function visitRuleset($rulesetNode){
+
+
+ if( $rulesetNode->root ){
+ return;
+ }
+
+ $allExtends = end($this->allExtendsStack);
+ $paths_len = count($rulesetNode->paths);
+
+ // look at each selector path in the ruleset, find any extend matches and then copy, find and replace
+ foreach($allExtends as $allExtend){
+ for($pathIndex = 0; $pathIndex < $paths_len; $pathIndex++ ){
+
+ // extending extends happens initially, before the main pass
+ if( isset($rulesetNode->extendOnEveryPath) && $rulesetNode->extendOnEveryPath ){
+ continue;
+ }
+
+ $selectorPath = $rulesetNode->paths[$pathIndex];
+
+ if( end($selectorPath)->extendList ){
+ continue;
+ }
+
+ $this->ExtendMatch( $rulesetNode, $allExtend, $selectorPath);
+
+ }
+ }
+ }
+
+
+ private function ExtendMatch( $rulesetNode, $extend, $selectorPath ){
+ $matches = $this->findMatch($extend, $selectorPath);
+
+ if( $matches ){
+ foreach($extend->selfSelectors as $selfSelector ){
+ $rulesetNode->paths[] = $this->extendSelector($matches, $selectorPath, $selfSelector);
+ }
+ }
+ }
+
+
+
+ private function findMatch($extend, $haystackSelectorPath ){
+
+
+ if( !$this->HasMatches($extend, $haystackSelectorPath) ){
+ return false;
+ }
+
+
+ //
+ // look through the haystack selector path to try and find the needle - extend.selector
+ // returns an array of selector matches that can then be replaced
+ //
+ $needleElements = $extend->selector->elements;
+ $potentialMatches = array();
+ $potentialMatches_len = 0;
+ $potentialMatch = null;
+ $matches = array();
+
+
+
+ // loop through the haystack elements
+ $haystack_path_len = count($haystackSelectorPath);
+ for($haystackSelectorIndex = 0; $haystackSelectorIndex < $haystack_path_len; $haystackSelectorIndex++ ){
+ $hackstackSelector = $haystackSelectorPath[$haystackSelectorIndex];
+
+ $haystack_elements_len = count($hackstackSelector->elements);
+ for($hackstackElementIndex = 0; $hackstackElementIndex < $haystack_elements_len; $hackstackElementIndex++ ){
+
+ $haystackElement = $hackstackSelector->elements[$hackstackElementIndex];
+
+ // if we allow elements before our match we can add a potential match every time. otherwise only at the first element.
+ if( $extend->allowBefore || ($haystackSelectorIndex === 0 && $hackstackElementIndex === 0) ){
+ $potentialMatches[] = array('pathIndex'=> $haystackSelectorIndex, 'index'=> $hackstackElementIndex, 'matched'=> 0, 'initialCombinator'=> $haystackElement->combinator);
+ $potentialMatches_len++;
+ }
+
+ for($i = 0; $i < $potentialMatches_len; $i++ ){
+
+ $potentialMatch = &$potentialMatches[$i];
+ $potentialMatch = $this->PotentialMatch( $potentialMatch, $needleElements, $haystackElement, $hackstackElementIndex );
+
+
+ // if we are still valid and have finished, test whether we have elements after and whether these are allowed
+ if( $potentialMatch && $potentialMatch['matched'] === $extend->selector->elements_len ){
+ $potentialMatch['finished'] = true;
+
+ if( !$extend->allowAfter && ($hackstackElementIndex+1 < $haystack_elements_len || $haystackSelectorIndex+1 < $haystack_path_len) ){
+ $potentialMatch = null;
+ }
+ }
+
+ // if null we remove, if not, we are still valid, so either push as a valid match or continue
+ if( $potentialMatch ){
+ if( $potentialMatch['finished'] ){
+ $potentialMatch['length'] = $extend->selector->elements_len;
+ $potentialMatch['endPathIndex'] = $haystackSelectorIndex;
+ $potentialMatch['endPathElementIndex'] = $hackstackElementIndex + 1; // index after end of match
+ $potentialMatches = array(); // we don't allow matches to overlap, so start matching again
+ $potentialMatches_len = 0;
+ $matches[] = $potentialMatch;
+ }
+ continue;
+ }
+
+ array_splice($potentialMatches, $i, 1);
+ $potentialMatches_len--;
+ $i--;
+ }
+ }
+ }
+
+ return $matches;
+ }
+
+
+ // Before going through all the nested loops, lets check to see if a match is possible
+ // Reduces Bootstrap 3.1 compile time from ~6.5s to ~5.6s
+ private function HasMatches($extend, $haystackSelectorPath){
+
+ if( !$extend->selector->cacheable ){
+ return true;
+ }
+
+ $first_el = $extend->selector->_oelements[0];
+
+ foreach($haystackSelectorPath as $hackstackSelector){
+ if( !$hackstackSelector->cacheable ){
+ return true;
+ }
+
+ if( in_array($first_el, $hackstackSelector->_oelements) ){
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+
+ /**
+ * @param integer $hackstackElementIndex
+ */
+ private function PotentialMatch( $potentialMatch, $needleElements, $haystackElement, $hackstackElementIndex ){
+
+
+ if( $potentialMatch['matched'] > 0 ){
+
+ // selectors add " " onto the first element. When we use & it joins the selectors together, but if we don't
+ // then each selector in haystackSelectorPath has a space before it added in the toCSS phase. so we need to work out
+ // what the resulting combinator will be
+ $targetCombinator = $haystackElement->combinator;
+ if( $targetCombinator === '' && $hackstackElementIndex === 0 ){
+ $targetCombinator = ' ';
+ }
+
+ if( $needleElements[ $potentialMatch['matched'] ]->combinator !== $targetCombinator ){
+ return null;
+ }
+ }
+
+ // if we don't match, null our match to indicate failure
+ if( !$this->isElementValuesEqual( $needleElements[$potentialMatch['matched'] ]->value, $haystackElement->value) ){
+ return null;
+ }
+
+ $potentialMatch['finished'] = false;
+ $potentialMatch['matched']++;
+
+ return $potentialMatch;
+ }
+
+
+ private function isElementValuesEqual( $elementValue1, $elementValue2 ){
+
+ if( $elementValue1 === $elementValue2 ){
+ return true;
+ }
+
+ if( is_string($elementValue1) || is_string($elementValue2) ) {
+ return false;
+ }
+
+ if( $elementValue1 instanceof Less_Tree_Attribute ){
+ return $this->isAttributeValuesEqual( $elementValue1, $elementValue2 );
+ }
+
+ $elementValue1 = $elementValue1->value;
+ if( $elementValue1 instanceof Less_Tree_Selector ){
+ return $this->isSelectorValuesEqual( $elementValue1, $elementValue2 );
+ }
+
+ return false;
+ }
+
+
+ /**
+ * @param Less_Tree_Selector $elementValue1
+ */
+ private function isSelectorValuesEqual( $elementValue1, $elementValue2 ){
+
+ $elementValue2 = $elementValue2->value;
+ if( !($elementValue2 instanceof Less_Tree_Selector) || $elementValue1->elements_len !== $elementValue2->elements_len ){
+ return false;
+ }
+
+ for( $i = 0; $i < $elementValue1->elements_len; $i++ ){
+
+ if( $elementValue1->elements[$i]->combinator !== $elementValue2->elements[$i]->combinator ){
+ if( $i !== 0 || ($elementValue1->elements[$i]->combinator || ' ') !== ($elementValue2->elements[$i]->combinator || ' ') ){
+ return false;
+ }
+ }
+
+ if( !$this->isElementValuesEqual($elementValue1->elements[$i]->value, $elementValue2->elements[$i]->value) ){
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+
+ /**
+ * @param Less_Tree_Attribute $elementValue1
+ */
+ private function isAttributeValuesEqual( $elementValue1, $elementValue2 ){
+
+ if( $elementValue1->op !== $elementValue2->op || $elementValue1->key !== $elementValue2->key ){
+ return false;
+ }
+
+ if( !$elementValue1->value || !$elementValue2->value ){
+ if( $elementValue1->value || $elementValue2->value ) {
+ return false;
+ }
+ return true;
+ }
+
+ $elementValue1 = ($elementValue1->value->value ? $elementValue1->value->value : $elementValue1->value );
+ $elementValue2 = ($elementValue2->value->value ? $elementValue2->value->value : $elementValue2->value );
+
+ return $elementValue1 === $elementValue2;
+ }
+
+
+ private function extendSelector($matches, $selectorPath, $replacementSelector){
+
+ //for a set of matches, replace each match with the replacement selector
+
+ $currentSelectorPathIndex = 0;
+ $currentSelectorPathElementIndex = 0;
+ $path = array();
+ $selectorPath_len = count($selectorPath);
+
+ for($matchIndex = 0, $matches_len = count($matches); $matchIndex < $matches_len; $matchIndex++ ){
+
+
+ $match = $matches[$matchIndex];
+ $selector = $selectorPath[ $match['pathIndex'] ];
+
+ $firstElement = new Less_Tree_Element(
+ $match['initialCombinator'],
+ $replacementSelector->elements[0]->value,
+ $replacementSelector->elements[0]->index,
+ $replacementSelector->elements[0]->currentFileInfo
+ );
+
+ if( $match['pathIndex'] > $currentSelectorPathIndex && $currentSelectorPathElementIndex > 0 ){
+ $last_path = end($path);
+ $last_path->elements = array_merge( $last_path->elements, array_slice( $selectorPath[$currentSelectorPathIndex]->elements, $currentSelectorPathElementIndex));
+ $currentSelectorPathElementIndex = 0;
+ $currentSelectorPathIndex++;
+ }
+
+ $newElements = array_merge(
+ array_slice($selector->elements, $currentSelectorPathElementIndex, ($match['index'] - $currentSelectorPathElementIndex) ) // last parameter of array_slice is different than the last parameter of javascript's slice
+ , array($firstElement)
+ , array_slice($replacementSelector->elements,1)
+ );
+
+ if( $currentSelectorPathIndex === $match['pathIndex'] && $matchIndex > 0 ){
+ $last_key = count($path)-1;
+ $path[$last_key]->elements = array_merge($path[$last_key]->elements,$newElements);
+ }else{
+ $path = array_merge( $path, array_slice( $selectorPath, $currentSelectorPathIndex, $match['pathIndex'] ));
+ $path[] = new Less_Tree_Selector( $newElements );
+ }
+
+ $currentSelectorPathIndex = $match['endPathIndex'];
+ $currentSelectorPathElementIndex = $match['endPathElementIndex'];
+ if( $currentSelectorPathElementIndex >= count($selectorPath[$currentSelectorPathIndex]->elements) ){
+ $currentSelectorPathElementIndex = 0;
+ $currentSelectorPathIndex++;
+ }
+ }
+
+ if( $currentSelectorPathIndex < $selectorPath_len && $currentSelectorPathElementIndex > 0 ){
+ $last_path = end($path);
+ $last_path->elements = array_merge( $last_path->elements, array_slice($selectorPath[$currentSelectorPathIndex]->elements, $currentSelectorPathElementIndex));
+ $currentSelectorPathIndex++;
+ }
+
+ $slice_len = $selectorPath_len - $currentSelectorPathIndex;
+ $path = array_merge($path, array_slice($selectorPath, $currentSelectorPathIndex, $slice_len));
+
+ return $path;
+ }
+
+
+ protected function visitMedia( $mediaNode ){
+ $newAllExtends = array_merge( $mediaNode->allExtends, end($this->allExtendsStack) );
+ $this->allExtendsStack[] = $this->doExtendChaining($newAllExtends, $mediaNode->allExtends);
+ }
+
+ protected function visitMediaOut(){
+ array_pop( $this->allExtendsStack );
+ }
+
+ protected function visitDirective( $directiveNode ){
+ $newAllExtends = array_merge( $directiveNode->allExtends, end($this->allExtendsStack) );
+ $this->allExtendsStack[] = $this->doExtendChaining($newAllExtends, $directiveNode->allExtends);
+ }
+
+ protected function visitDirectiveOut(){
+ array_pop($this->allExtendsStack);
+ }
+
+}
\ No newline at end of file
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Visitor/toCSS.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Visitor/toCSS.php
new file mode 100644
index 000000000..8aaca9637
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/Visitor/toCSS.php
@@ -0,0 +1,292 @@
+visitObj($root);
+ }
+
+ public function visitRule( $ruleNode ){
+ if( $ruleNode->variable ){
+ return array();
+ }
+ return $ruleNode;
+ }
+
+ public function visitMixinDefinition($mixinNode){
+ // mixin definitions do not get eval'd - this means they keep state
+ // so we have to clear that state here so it isn't used if toCSS is called twice
+ $mixinNode->frames = array();
+ return array();
+ }
+
+ public function visitExtend(){
+ return array();
+ }
+
+ public function visitComment( $commentNode ){
+ if( $commentNode->isSilent() ){
+ return array();
+ }
+ return $commentNode;
+ }
+
+ public function visitMedia( $mediaNode, &$visitDeeper ){
+ $mediaNode->accept($this);
+ $visitDeeper = false;
+
+ if( !$mediaNode->rules ){
+ return array();
+ }
+ return $mediaNode;
+ }
+
+ public function visitDirective( $directiveNode ){
+ if( isset($directiveNode->currentFileInfo['reference']) && (!property_exists($directiveNode,'isReferenced') || !$directiveNode->isReferenced) ){
+ return array();
+ }
+ if( $directiveNode->name === '@charset' ){
+ // Only output the debug info together with subsequent @charset definitions
+ // a comment (or @media statement) before the actual @charset directive would
+ // be considered illegal css as it has to be on the first line
+ if( isset($this->charset) && $this->charset ){
+
+ //if( $directiveNode->debugInfo ){
+ // $comment = new Less_Tree_Comment('/* ' . str_replace("\n",'',$directiveNode->toCSS())." */\n");
+ // $comment->debugInfo = $directiveNode->debugInfo;
+ // return $this->visit($comment);
+ //}
+
+
+ return array();
+ }
+ $this->charset = true;
+ }
+ return $directiveNode;
+ }
+
+ public function checkPropertiesInRoot( $rulesetNode ){
+
+ if( !$rulesetNode->firstRoot ){
+ return;
+ }
+
+ foreach($rulesetNode->rules as $ruleNode){
+ if( $ruleNode instanceof Less_Tree_Rule && !$ruleNode->variable ){
+ $msg = "properties must be inside selector blocks, they cannot be in the root. Index ".$ruleNode->index.($ruleNode->currentFileInfo ? (' Filename: '.$ruleNode->currentFileInfo['filename']) : null);
+ throw new Less_Exception_Compiler($msg);
+ }
+ }
+ }
+
+
+ public function visitRuleset( $rulesetNode, &$visitDeeper ){
+
+ $visitDeeper = false;
+
+ $this->checkPropertiesInRoot( $rulesetNode );
+
+ if( $rulesetNode->root ){
+ return $this->visitRulesetRoot( $rulesetNode );
+ }
+
+ $rulesets = array();
+ $rulesetNode->paths = $this->visitRulesetPaths($rulesetNode);
+
+
+ // Compile rules and rulesets
+ $nodeRuleCnt = $rulesetNode->rules?count($rulesetNode->rules):0;
+ for( $i = 0; $i < $nodeRuleCnt; ){
+ $rule = $rulesetNode->rules[$i];
+
+ if( property_exists($rule,'rules') ){
+ // visit because we are moving them out from being a child
+ $rulesets[] = $this->visitObj($rule);
+ array_splice($rulesetNode->rules,$i,1);
+ $nodeRuleCnt--;
+ continue;
+ }
+ $i++;
+ }
+
+
+ // accept the visitor to remove rules and refactor itself
+ // then we can decide now whether we want it or not
+ if( $nodeRuleCnt > 0 ){
+ $rulesetNode->accept($this);
+
+ if( $rulesetNode->rules ){
+
+ if( count($rulesetNode->rules) > 1 ){
+ $this->_mergeRules( $rulesetNode->rules );
+ $this->_removeDuplicateRules( $rulesetNode->rules );
+ }
+
+ // now decide whether we keep the ruleset
+ if( $rulesetNode->paths ){
+ //array_unshift($rulesets, $rulesetNode);
+ array_splice($rulesets,0,0,array($rulesetNode));
+ }
+ }
+
+ }
+
+
+ if( count($rulesets) === 1 ){
+ return $rulesets[0];
+ }
+ return $rulesets;
+ }
+
+
+ /**
+ * Helper function for visitiRuleset
+ *
+ * return array|Less_Tree_Ruleset
+ */
+ private function visitRulesetRoot( $rulesetNode ){
+ $rulesetNode->accept( $this );
+ if( $rulesetNode->firstRoot || $rulesetNode->rules ){
+ return $rulesetNode;
+ }
+ return array();
+ }
+
+
+ /**
+ * Helper function for visitRuleset()
+ *
+ * @return array
+ */
+ private function visitRulesetPaths($rulesetNode){
+
+ $paths = array();
+ foreach($rulesetNode->paths as $p){
+ if( $p[0]->elements[0]->combinator === ' ' ){
+ $p[0]->elements[0]->combinator = '';
+ }
+
+ foreach($p as $pi){
+ if( $pi->getIsReferenced() && $pi->getIsOutput() ){
+ $paths[] = $p;
+ break;
+ }
+ }
+ }
+
+ return $paths;
+ }
+
+ protected function _removeDuplicateRules( &$rules ){
+ // remove duplicates
+ $ruleCache = array();
+ for( $i = count($rules)-1; $i >= 0 ; $i-- ){
+ $rule = $rules[$i];
+ if( $rule instanceof Less_Tree_Rule || $rule instanceof Less_Tree_NameValue ){
+
+ if( !isset($ruleCache[$rule->name]) ){
+ $ruleCache[$rule->name] = $rule;
+ }else{
+ $ruleList =& $ruleCache[$rule->name];
+
+ if( $ruleList instanceof Less_Tree_Rule || $ruleList instanceof Less_Tree_NameValue ){
+ $ruleList = $ruleCache[$rule->name] = array( $ruleCache[$rule->name]->toCSS() );
+ }
+
+ $ruleCSS = $rule->toCSS();
+ if( array_search($ruleCSS,$ruleList) !== false ){
+ array_splice($rules,$i,1);
+ }else{
+ $ruleList[] = $ruleCSS;
+ }
+ }
+ }
+ }
+ }
+
+ protected function _mergeRules( &$rules ){
+ $groups = array();
+
+ //obj($rules);
+
+ $rules_len = count($rules);
+ for( $i = 0; $i < $rules_len; $i++ ){
+ $rule = $rules[$i];
+
+ if( ($rule instanceof Less_Tree_Rule) && $rule->merge ){
+
+ $key = $rule->name;
+ if( $rule->important ){
+ $key .= ',!';
+ }
+
+ if( !isset($groups[$key]) ){
+ $groups[$key] = array();
+ }else{
+ array_splice($rules, $i--, 1);
+ $rules_len--;
+ }
+
+ $groups[$key][] = $rule;
+ }
+ }
+
+
+ foreach($groups as $parts){
+
+ if( count($parts) > 1 ){
+ $rule = $parts[0];
+ $spacedGroups = array();
+ $lastSpacedGroup = array();
+ $parts_mapped = array();
+ foreach($parts as $p){
+ if( $p->merge === '+' ){
+ if( $lastSpacedGroup ){
+ $spacedGroups[] = self::toExpression($lastSpacedGroup);
+ }
+ $lastSpacedGroup = array();
+ }
+ $lastSpacedGroup[] = $p;
+ }
+
+ $spacedGroups[] = self::toExpression($lastSpacedGroup);
+ $rule->value = self::toValue($spacedGroups);
+ }
+ }
+
+ }
+
+ public static function toExpression($values){
+ $mapped = array();
+ foreach($values as $p){
+ $mapped[] = $p->value;
+ }
+ return new Less_Tree_Expression( $mapped );
+ }
+
+ public static function toValue($values){
+ //return new Less_Tree_Value($values); ??
+
+ $mapped = array();
+ foreach($values as $p){
+ $mapped[] = $p;
+ }
+ return new Less_Tree_Value($mapped);
+ }
+}
+
diff --git a/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/VisitorReplacing.php b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/VisitorReplacing.php
new file mode 100644
index 000000000..5923170e2
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/libs/lessphp/lib/Less/VisitorReplacing.php
@@ -0,0 +1,75 @@
+type;
+ if( isset($this->_visitFnCache[$funcName]) ){
+
+ $visitDeeper = true;
+ $node = $this->$funcName( $node, $visitDeeper );
+
+ if( $node ){
+ if( $visitDeeper && is_object($node) ){
+ $node->accept($this);
+ }
+
+ $funcName = $funcName . "Out";
+ if( isset($this->_visitFnCache[$funcName]) ){
+ $this->$funcName( $node );
+ }
+ }
+
+ }else{
+ $node->accept($this);
+ }
+
+ return $node;
+ }
+
+ public function visitArray( $nodes ){
+
+ $newNodes = array();
+ foreach($nodes as $node){
+ $evald = $this->visitObj($node);
+ if( $evald ){
+ if( is_array($evald) ){
+ self::flatten($evald,$newNodes);
+ }else{
+ $newNodes[] = $evald;
+ }
+ }
+ }
+ return $newNodes;
+ }
+
+ public function flatten( $arr, &$out ){
+
+ foreach($arr as $item){
+ if( !is_array($item) ){
+ $out[] = $item;
+ continue;
+ }
+
+ foreach($item as $nestedItem){
+ if( is_array($nestedItem) ){
+ self::flatten( $nestedItem, $out);
+ }else{
+ $out[] = $nestedItem;
+ }
+ }
+ }
+
+ return $out;
+ }
+
+}
+
+
diff --git a/src/packages/jbuniversal/jbuniversal/framework/models/jbmodel.item.php b/src/packages/jbuniversal/jbuniversal/framework/models/jbmodel.item.php
index 98e96b41a..9f9a6b4b5 100644
--- a/src/packages/jbuniversal/jbuniversal/framework/models/jbmodel.item.php
+++ b/src/packages/jbuniversal/jbuniversal/framework/models/jbmodel.item.php
@@ -136,11 +136,27 @@ public function getList($appId = null, $catId = null, $typeId = null, $options =
$select->where('tItem.state = ?', 0);
}
+ // check elements
+ if ($options->get('elements')) {
+ $elements = $options->get('elements');
+
+ $i = 0;
+
+ foreach ($elements as $element) {
+ $mode = ($element['mode'] == 'equal') ? 'REGEXP' : 'NOT REGEXP';
+
+ $select->innerJoin(ZOO_TABLE_SEARCH . ' AS tSearchItem'.$i.' ON tItem.id = tSearchItem'.$i.'.item_id');
+ $select->where('(tSearchItem'.$i.'.element_id = "'.$element['element'].'" AND tSearchItem'.$i.'.value '.$mode.' "'.$element['value'].'")');
+
+ $i++;
+ }
+ }
+
$order = $options->get('order', 'id');
if (isset($order)) {
$select->order($this->app->jborder->get($options->get('order', 'id'), 'tItem'));
}
-
+
$rows = $this->_query($select);
if (!empty($rows)) {
@@ -154,6 +170,106 @@ public function getList($appId = null, $catId = null, $typeId = null, $options =
return array();
}
+ /**
+ * Get Zoo items
+ * @param int $appId
+ * @param int|array $catId
+ * @param string $typeId
+ * @param array $options
+ * @return array
+ */
+ public function getListCount($appId = null, $catId = null, $typeId = null, $options = array())
+ {
+ $options = $this->app->data->create($options);
+
+ // create select
+ $select = $this->_getSelect()
+ ->select('COUNT(tItem.id) AS count')
+ ->from(ZOO_TABLE_ITEM . ' AS tItem');
+
+
+ // check type
+ if (!empty($typeId)) {
+ $typeId = (array)$typeId;
+ $select->where('tItem.type IN ("' . implode('", "', $typeId) . '")');
+ }
+
+ // check appId
+ if (is_array($appId)) {
+ $select->where('tItem.application_id IN (' . implode(',', $appId) . ')');
+ } elseif ((int)$appId) {
+ $select->where('tItem.application_id = ?', (int)$appId);
+ }
+
+ // check category
+ $catId = array_filter((array)$catId, function ($value) {
+ return $value > 0 || $value == -1 || $value === 0 || $value === '0';
+ });
+
+ if (
+ !empty($catId) &&
+ !in_array('-1', $catId)
+ ) {
+ $select->innerJoin(ZOO_TABLE_CATEGORY_ITEM . ' AS tCategoryItem ON tItem.id = tCategoryItem.item_id');
+
+ $subcatId = array();
+ if ((int)$options->get('category_nested')) {
+ $subcatId = JBModelCategory::model()->getNestedCategories($catId);
+ }
+
+ $catId += $subcatId;
+
+ if (!empty($catId)) {
+ $select->where('tCategoryItem.category_id IN ("' . implode('", "', $catId) . '")');
+ }
+
+ }
+
+ if ($options->get('id')) {
+ $idList = (array)$options->get('id');
+ $select->where('tItem.id IN (' . implode(',', $idList) . ')');
+ }
+
+ // check access
+ if ($options->get('user')) {
+ $select->where('tItem.' . $this->app->user->getDBAccessString());
+ }
+
+ // check status
+ if ($options->get('published') == 1) {
+ $select
+ ->where('tItem.state = ?', 1)
+ ->where('(tItem.publish_up = ' . $this->_dbNull . ' OR tItem.publish_up <= ' . $this->_dbNow . ')')
+ ->where('(tItem.publish_down = ' . $this->_dbNull . ' OR tItem.publish_down >= ' . $this->_dbNow . ')');
+
+ } else if ($options->get('state') == 2) {
+ $select->where('tItem.state = ?', 1);
+
+ } else if ($options->get('state') == 3) {
+ $select->where('tItem.state = ?', 0);
+ }
+
+ // check elements
+ if ($options->get('elements')) {
+ $elements = $options->get('elements');
+
+ $i = 0;
+
+ foreach ($elements as $element) {
+ $mode = ($element['mode'] == 'equal') ? 'REGEXP' : 'NOT REGEXP';
+
+ $select->innerJoin(ZOO_TABLE_SEARCH . ' AS tSearchItem'.$i.' ON tItem.id = tSearchItem'.$i.'.item_id');
+ $select->where('(tSearchItem'.$i.'.element_id = "'.$element['element'].'" AND tSearchItem'.$i.'.value '.$mode.' "'.$element['value'].'")');
+
+ $i++;
+ }
+ }
+
+ $result = $this->fetchRow($select);
+
+ return (int)$result->count;
+ }
+
/**
* Get item categories
* @param $itemId
diff --git a/src/packages/jbuniversal/jbuniversal/framework/models/jbmodel.order.php b/src/packages/jbuniversal/jbuniversal/framework/models/jbmodel.order.php
index fe029f887..90189ee30 100644
--- a/src/packages/jbuniversal/jbuniversal/framework/models/jbmodel.order.php
+++ b/src/packages/jbuniversal/jbuniversal/framework/models/jbmodel.order.php
@@ -106,6 +106,7 @@ public function save(JBCartOrder $order, $silentMode = false)
'items' => $order->getItems(false),
'fields' => $order->getFields(),
'shippingfields' => $order->getShippingFields(),
+ 'track' => $order->track,
'modifiers' => $order->getModifiersData(),
'params' => $params,
'comment' => $order->comment,
diff --git a/src/packages/jbuniversal/jbuniversal/framework/views/jborder/_edit_block_track.php b/src/packages/jbuniversal/jbuniversal/framework/views/jborder/_edit_block_track.php
new file mode 100644
index 000000000..c935b1ab3
--- /dev/null
+++ b/src/packages/jbuniversal/jbuniversal/framework/views/jborder/_edit_block_track.php
@@ -0,0 +1,29 @@
+
+ */
+
+// no direct access
+defined('_JEXEC') or die('Restricted access');
+
+?>
+
+
+
+
+
+
+ app->jbhtml->text('order[track]', $order->track, '', ''); ?>
+
+
+
\ No newline at end of file
diff --git a/src/packages/jbuniversal/jbuniversal/framework/views/jborder/_edit_table_modifiers.php b/src/packages/jbuniversal/jbuniversal/framework/views/jborder/_edit_table_modifiers.php
index 91c0f9375..20c07ba0c 100644
--- a/src/packages/jbuniversal/jbuniversal/framework/views/jborder/_edit_table_modifiers.php
+++ b/src/packages/jbuniversal/jbuniversal/framework/views/jborder/_edit_table_modifiers.php
@@ -27,8 +27,8 @@
if ($i == 1) { ?>
-
-
+ 1) ? 'rowspan="'.(count($modifiers) - 1).'"' : '' ; ?> class="noborder-btm">
+ 1) ? 'rowspan="'.(count($modifiers) - 1).'"' : '' ; ?>>
()
diff --git a/src/packages/jbuniversal/jbuniversal/framework/views/jborder/edit.php b/src/packages/jbuniversal/jbuniversal/framework/views/jborder/edit.php
index fa1991fa7..efc097a96 100644
--- a/src/packages/jbuniversal/jbuniversal/framework/views/jborder/edit.php
+++ b/src/packages/jbuniversal/jbuniversal/framework/views/jborder/edit.php
@@ -84,6 +84,10 @@
'order' => $order,
'shipping' => $shipping,
));
+
+ echo $this->partial('edit_block_track', array(
+ 'order' => $order,
+ ));
}
?>
diff --git a/src/packages/jbuniversal/jbuniversal/language/en-GB/en-GB.com_jbzoo.ini b/src/packages/jbuniversal/jbuniversal/language/en-GB/en-GB.com_jbzoo.ini
index 5e0688c1d..d7f185024 100644
--- a/src/packages/jbuniversal/jbuniversal/language/en-GB/en-GB.com_jbzoo.ini
+++ b/src/packages/jbuniversal/jbuniversal/language/en-GB/en-GB.com_jbzoo.ini
@@ -320,6 +320,10 @@ JBZOO_ADMIN_POSITION_NOTIFICATION_ORDER_BEFORESAVE = "Before order sav
JBZOO_ADMIN_POSITION_NOTIFICATION_ORDER_BEFORESAVE_DESC = "Validation has already run successfully, you need to save only"
JBZOO_ADMIN_POSITION_NOTIFICATION_ORDER_CHANGEQUANTITY = "Item quantity in the cart has been changed"
JBZOO_ADMIN_POSITION_NOTIFICATION_ORDER_CHANGEQUANTITY_DESC = "For example, via +/- widget"
+JBZOO_ADMIN_POSITION_NOTIFICATION_ORDER_ADDTRACK = "Track number appeared"
+JBZOO_ADMIN_POSITION_NOTIFICATION_ORDER_ADDTRACK_DESC = "when the track number first appears"
+JBZOO_ADMIN_POSITION_NOTIFICATION_ORDER_CHANGETRACK = "Track number changed"
+JBZOO_ADMIN_POSITION_NOTIFICATION_ORDER_CHANGETRACK_DESC = "every time you change the track number"
JBZOO_ADMIN_POSITION_NOTIFICATION_ORDER_ORDERSTATUS = "When changing general order status"
JBZOO_ADMIN_POSITION_NOTIFICATION_ORDER_ORDERSTATUS_DESC = "By robot or administrator"
JBZOO_ADMIN_POSITION_NOTIFICATION_ORDER_PAYMENTCALLBACK = "Payment robot entry"
@@ -1486,6 +1490,8 @@ JBZOO_ELEMENT_EMAIL_USER_INFOTYPE_NAME = "User name"
JBZOO_ELEMENT_EMAIL_USER_INFOTYPE_USERID = "User ID"
JBZOO_ELEMENT_EMAIL_USER_INFOTYPE_USERNAME = "Login"
JBZOO_ELEMENT_EMAIL_USER_NAME = "User (current)"
+JBZOO_ELEMENT_EMAIL_TRACK_NAME = "Order: Track number"
+JBZOO_ELEMENT_EMAIL_TRACK_DESC = "Order track number"
JBZOO_ELEMENT_HOOK_BALANCEREDUCE_DESC = "Changes the remains of items in stock, it is usually put on the creation of an order or its payment"
JBZOO_ELEMENT_HOOK_BALANCEREDUCE_NAME = "Reduce remains"
JBZOO_ELEMENT_HOOK_JBADVERT_DESC = "Starts the JBAdvert element performance in the material that will change the advert status"
@@ -3268,6 +3274,7 @@ JBZOO_ORDER_MACROS_ORDER_SHIPPING_NAME = "Shipping method"
JBZOO_ORDER_MACROS_ORDER_SHIPPING_STAT = "Shipping status"
JBZOO_ORDER_MACROS_ORDER_STATUS = "General order status"
JBZOO_ORDER_MACROS_ORDER_TOTAL = "Total sum"
+JBZOO_ORDER_MACROS_ORDER_TRACK = "Track number"
JBZOO_ORDER_MACROS_SHOP_ADDRESS = "Store address"
JBZOO_ORDER_MACROS_SHOP_CITY = "Store city"
JBZOO_ORDER_MACROS_SHOP_COUNTRY = "Store country"
@@ -3765,22 +3772,26 @@ JBZOO_CONFIG_ZOO_CACHE_WRITABLE_DISABLED_DESC = "Disables the verification of
JBZOO_ZOO_MEDIA_IS_WRITABLE = "Disable check media folder"
JBZOO_CONFIG_ZOO_MEDIA_IS_WRITABLE = "Disable media rights check"
JBZOO_CONFIG_ZOO_MEDIA_IS_WRITABLE_DESC = "Disables the verification of the rights to write media folders"
-JBZOO_CONFIG_MY_ITEMS_HACKS_ON = "Enable your YML file configuration"
-JBZOO_CONFIG_MY_ITEMS_HACKS = "Manually configuring YML export"
-JBZOO_CONFIG_MY_ITEMS_HACKS_EL = "Manually configure YML export by ELEMENT_ID"
-JBZOO_EL_MODE = "Filtering mode"
-JBZOO_EL_MODE_DESC = "Select the filter operation mode: 1. Does not contain this value in the field 2. Only items with this value"
-JBZOO_CONFIG_YML_ONLY_MY_LISTIMTES_CUSTOM_RULES_MODE = "Enable your YML file configuration"
-JBZOO_CONFIG_YML_ONLY_MY_LISTIMTES_CUSTOM_RULES_MODE_CUSTOM = "Enable the option to configure the YML file"
-JBZOO_CONFIG_YML_ONLY_MY_PHP_CUSTOM_RULES_MODE_DESC = "Select YES - to activate the settings below"
-JBZOO_CONFIG_MY_ITEMS_HACKS_LIST = "Exporting only select items"
-JBZOO_CONFIG_YML_ONLY_MY_LISTITEMS_RULES = "Exporting only select items"
-JBZOO_CONFIG_YML_ONLY_MY_LISTITEMS_RULES_DESC = "Enter Items, separated by commas, only those materials that should be included in the YML file"
-JBZOO_CONFIG_YML_ONLY_MY_ITEMS = "Enter the ELEMENT_ID of the field"
-JBZOO_CONFIG_YML_ONLY_MY_ITEMS_DESC = "Enter ELEMENT_ID by which the value in the selection will be checked in the YML file"
-JBZOO_CONFIG_YML_ONLY_MY_CUSTOMRULES_MODE = "Its value in the array"
-JBZOO_CONFIG_YML_ONLY_MY_CUSTOMRULES_MODE_DESC = "Enable if you do not filter by checkbox"
-JBZOO_CONFIG_YML_ONLY_MY_ITEMS_VALUE_CUSTOMRULES = "Path in array"
-JBZOO_CONFIG_YML_ONLY_MY_ITEMS_VALUE_CUSTOMRULES_DESC = "Find in the array this path to the value"
-JBZOO_CONFIG_YML_ONLY_MY_ITEMS_VALUE = "Value in the element"
-JBZOO_CONFIG_YML_ONLY_MY_ITEMS_VALUE_DESC = "Check the value of the element for this value and filter the YML file"
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; YML custom rules ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+JBZOO_CONFIG_YML_CUSTOM_RULES = "Enable custom conditions"
+JBZOO_CONFIG_YML_CUSTOM_RULES_DESC = "Select YES to activate the settings below"
+JBZOO_CONFIG_YML_CUSTOM_RULES_SEPARATOR = "Enable YML Setting"
+JBZOO_CONFIG_YML_CUSTOM_ITEMS = "Exporting only select items"
+JBZOO_CONFIG_YML_CUSTOM_ITEMS_DESC = "Enter Items IDs, separated by commas, only those materials that should be included in the YML file"
+JBZOO_CONFIG_YML_CUSTOM_CONDITIONS = "Search Terms"
+JBZOO_CONFIG_YML_CUSTOM_CONDITIONS_DESC = "Search conditions for items"
+JBZOO_CONFIG_YML_CUSTOM_CONDITIONS_ELEMENTS = "Select an element"
+JBZOO_CONFIG_YML_CUSTOM_CONDITIONS_ELEMENTS_DESC = "Select an element"
+JBZOO_CONFIG_YML_CUSTOM_CONDITIONS_MODE = "Filtering mode"
+JBZOO_CONFIG_YML_CUSTOM_CONDITIONS_MODE_DESC = "Filtering mode"
+JBZOO_CONFIG_YML_CUSTOM_CONDITIONS_MODE_EQUAL = "Equal"
+JBZOO_CONFIG_YML_CUSTOM_CONDITIONS_MODE_NOT_EQUAL = "Not Equal"
+JBZOO_CONFIG_YML_CUSTOM_CONDITIONS_VALUE = "Element value"
+JBZOO_CONFIG_YML_CUSTOM_CONDITIONS_VALUE_DESC = "Element value (full value from filter or from the render of the element, etc.)"
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Track Number ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+JBZOO_ORDER_TRACK_TITLE = "Order tracking"
+JBZOO_ORDER_TRACK = "Track number"
diff --git a/src/packages/jbuniversal/jbuniversal/language/ru-RU/ru-RU.com_jbzoo.ini b/src/packages/jbuniversal/jbuniversal/language/ru-RU/ru-RU.com_jbzoo.ini
index be1965f7f..e51420301 100644
--- a/src/packages/jbuniversal/jbuniversal/language/ru-RU/ru-RU.com_jbzoo.ini
+++ b/src/packages/jbuniversal/jbuniversal/language/ru-RU/ru-RU.com_jbzoo.ini
@@ -344,6 +344,10 @@ JBZOO_ADMIN_POSITION_NOTIFICATION_ORDER_UPDATED = "Изменени
JBZOO_ADMIN_POSITION_NOTIFICATION_ORDER_UPDATED_DESC = "любое(!) сохранение заказа в базу данных кроме создания нового"
JBZOO_ADMIN_POSITION_NOTIFICATION_ORDER_UPDATEITEM = "Товар изменился в корзине"
JBZOO_ADMIN_POSITION_NOTIFICATION_ORDER_UPDATEITEM_DESC = "Пользователь что-то сделал с товаром в корзине"
+JBZOO_ADMIN_POSITION_NOTIFICATION_ORDER_ADDTRACK = "Появился трек номер"
+JBZOO_ADMIN_POSITION_NOTIFICATION_ORDER_ADDTRACK_DESC = "при первом появлении трек-номера"
+JBZOO_ADMIN_POSITION_NOTIFICATION_ORDER_CHANGETRACK = "Изменился трек номер"
+JBZOO_ADMIN_POSITION_NOTIFICATION_ORDER_CHANGETRACK_DESC = "при каждом следующем изменении трек номера"
JBZOO_ADMIN_POSITION_PAYMENT_LIST = "Доступные способы оплаты"
JBZOO_ADMIN_POSITION_PAYMENT_LIST_DESC = "и их настройки"
JBZOO_ADMIN_POSITION_PRICEFILTERTMPL_LIST = "Вывод элементов цены в форме фильтра"
@@ -1486,6 +1490,8 @@ JBZOO_ELEMENT_EMAIL_USER_INFOTYPE_NAME = "Имя польз
JBZOO_ELEMENT_EMAIL_USER_INFOTYPE_USERID = "User ID"
JBZOO_ELEMENT_EMAIL_USER_INFOTYPE_USERNAME = "Логин"
JBZOO_ELEMENT_EMAIL_USER_NAME = "Пользователь (текущий)"
+JBZOO_ELEMENT_EMAIL_TRACK_NAME = "Заказ: Трек номер"
+JBZOO_ELEMENT_EMAIL_TRACK_DESC = "Трек номер заказа"
JBZOO_ELEMENT_HOOK_BALANCEREDUCE_DESC = "Изменяет остаток товара на складе, обычно его ставят на создание заказа или его оплату"
JBZOO_ELEMENT_HOOK_BALANCEREDUCE_NAME = "Уменьшить остаток"
JBZOO_ELEMENT_HOOK_JBADVERT_DESC = "Запускает выполнение элемента JBAdvert в материале, который изменят состояние объявления"
@@ -3268,6 +3274,7 @@ JBZOO_ORDER_MACROS_ORDER_SHIPPING_NAME = "Способ доставки"
JBZOO_ORDER_MACROS_ORDER_SHIPPING_STAT = "Статус доставки"
JBZOO_ORDER_MACROS_ORDER_STATUS = "Общий статус заказа"
JBZOO_ORDER_MACROS_ORDER_TOTAL = "Сумма заказа"
+JBZOO_ORDER_MACROS_ORDER_TRACK = "Трек номер"
JBZOO_ORDER_MACROS_SHOP_ADDRESS = "Адрес магазина"
JBZOO_ORDER_MACROS_SHOP_CITY = "Город магазина"
JBZOO_ORDER_MACROS_SHOP_COUNTRY = "Страна магазина"
@@ -3749,6 +3756,7 @@ JBZOO_YML_URL_SETTINGS = "Настройки"
JBZOO_YML_URL_SETTINGS_DESC = "Яндекс Маркет"
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ZooHacks ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
JBZOO_CONFIG_ZOO_HACKS = "Настройки Zoo и JBZoo"
JBZOO_HIT_DISABLED = "Отключить просмотры (хиты)"
JBZOO_CONFIG_HIT_DISABLED = "Отключить хиты"
@@ -3765,22 +3773,26 @@ JBZOO_CONFIG_ZOO_CACHE_WRITABLE_DISABLED_DESC = "Отключается про
JBZOO_ZOO_MEDIA_IS_WRITABLE = "Отключить проверку media папки"
JBZOO_CONFIG_ZOO_MEDIA_IS_WRITABLE = "Отключить проверка прав media папки"
JBZOO_CONFIG_ZOO_MEDIA_IS_WRITABLE_DESC = "Отключается проверка прав на запись media папки"
-JBZOO_CONFIG_MY_ITEMS_HACKS_ON = "Включить свою настройку YML файла"
-JBZOO_CONFIG_MY_ITEMS_HACKS = "Ручная настройка экспорта YML"
-JBZOO_CONFIG_MY_ITEMS_HACKS_EL = "Ручная настройка экспорта YML по Элементу"
-JBZOO_EL_MODE = "Режим работы фильтрации"
-JBZOO_EL_MODE_DESC = "Выбрать режим работы фильтра: 1. не содержит в поле данное значение 2. Только items с данным значением"
-JBZOO_CONFIG_YML_ONLY_MY_LISTIMTES_CUSTOM_RULES_MODE = "Включить свою настройку YML файла"
-JBZOO_CONFIG_YML_ONLY_MY_LISTIMTES_CUSTOM_RULES_MODE_CUSTOM = "Включить опцию настройку YML файла"
-JBZOO_CONFIG_YML_ONLY_MY_PHP_CUSTOM_RULES_MODE_DESC = "Выбрать ДА - чтобы активировать настройки ниже"
-JBZOO_CONFIG_MY_ITEMS_HACKS_LIST = "Экспорт только выборочных items"
-JBZOO_CONFIG_YML_ONLY_MY_LISTITEMS_RULES = "Экспорт только выборочных items"
-JBZOO_CONFIG_YML_ONLY_MY_LISTITEMS_RULES_DESC = "Укажите, через запятую только те материалы, которые должны попасть в YML файл"
-JBZOO_CONFIG_YML_ONLY_MY_ITEMS = "Укажите ELEMENT_ID поля"
-JBZOO_CONFIG_YML_ONLY_MY_ITEMS_DESC = "Укажите ELEMENT_ID по которому будет проверяться значение в выборке в YML файл"
-JBZOO_CONFIG_YML_ONLY_MY_CUSTOMRULES_MODE = "Свое значение в массиве"
-JBZOO_CONFIG_YML_ONLY_MY_CUSTOMRULES_MODE_DESC = "Включите, если Вы будете фильтровать не по элементу checkbox"
-JBZOO_CONFIG_YML_ONLY_MY_ITEMS_VALUE_CUSTOMRULES = "Путь в массиве"
-JBZOO_CONFIG_YML_ONLY_MY_ITEMS_VALUE_CUSTOMRULES_DESC = "Найти в массиве данный путь к значению"
-JBZOO_CONFIG_YML_ONLY_MY_ITEMS_VALUE = "Значение в элементе"
-JBZOO_CONFIG_YML_ONLY_MY_ITEMS_VALUE_DESC = "Проверять значение элемента на данное значение и фильтровать YML файл"
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; YML custom rules ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+JBZOO_CONFIG_YML_CUSTOM_RULES = "Включить условия поиска"
+JBZOO_CONFIG_YML_CUSTOM_RULES_DESC = "Выберите ДА, чтобы активировать настройки ниже"
+JBZOO_CONFIG_YML_CUSTOM_RULES_SEPARATOR = "Включить настройку YML"
+JBZOO_CONFIG_YML_CUSTOM_ITEMS = "Экспорт только выборочных материалов"
+JBZOO_CONFIG_YML_CUSTOM_ITEMS_DESC = "Укажите, через запятую только те материалы, которые должны попасть в YML файл"
+JBZOO_CONFIG_YML_CUSTOM_CONDITIONS = "Условия поиска"
+JBZOO_CONFIG_YML_CUSTOM_CONDITIONS_DESC = "Условия поиска материалов, которые должны быть в ленте"
+JBZOO_CONFIG_YML_CUSTOM_CONDITIONS_ELEMENTS = "Выберите элемент"
+JBZOO_CONFIG_YML_CUSTOM_CONDITIONS_ELEMENTS_DESC = "Выберите элемент"
+JBZOO_CONFIG_YML_CUSTOM_CONDITIONS_MODE = "Выберите режим сравнения"
+JBZOO_CONFIG_YML_CUSTOM_CONDITIONS_MODE_DESC = "Выберите режим сравнения"
+JBZOO_CONFIG_YML_CUSTOM_CONDITIONS_MODE_EQUAL = "Равно"
+JBZOO_CONFIG_YML_CUSTOM_CONDITIONS_MODE_NOT_EQUAL = "Не равно"
+JBZOO_CONFIG_YML_CUSTOM_CONDITIONS_VALUE = "Укажите значение"
+JBZOO_CONFIG_YML_CUSTOM_CONDITIONS_VALUE_DESC = "Укажите значение (полное значение - из фильтра или из рендера элемента и т.д.)"
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Track Number ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+JBZOO_ORDER_TRACK_TITLE = "Отслеживание заказа"
+JBZOO_ORDER_TRACK = "Трек номер"
diff --git a/src/packages/jbuniversal/jbuniversal/language/uk-UA/uk-UA.com_jbzoo.ini b/src/packages/jbuniversal/jbuniversal/language/uk-UA/uk-UA.com_jbzoo.ini
index e8cc650a2..d92e13bba 100644
--- a/src/packages/jbuniversal/jbuniversal/language/uk-UA/uk-UA.com_jbzoo.ini
+++ b/src/packages/jbuniversal/jbuniversal/language/uk-UA/uk-UA.com_jbzoo.ini
@@ -344,6 +344,10 @@ JBZOO_ADMIN_POSITION_NOTIFICATION_ORDER_UPDATED = "Зміна за
JBZOO_ADMIN_POSITION_NOTIFICATION_ORDER_UPDATED_DESC = "будь-яке(!) збереження замовлення в базу данних окрім створення нового"
JBZOO_ADMIN_POSITION_NOTIFICATION_ORDER_UPDATEITEM = "Товар змінився у кошику"
JBZOO_ADMIN_POSITION_NOTIFICATION_ORDER_UPDATEITEM_DESC = "Користувач щось зробив з товаром в кошику"
+JBZOO_ADMIN_POSITION_NOTIFICATION_ORDER_ADDTRACK = "З'явився трек номер"
+JBZOO_ADMIN_POSITION_NOTIFICATION_ORDER_ADDTRACK_DESC = "при першій появі трек-номера"
+JBZOO_ADMIN_POSITION_NOTIFICATION_ORDER_CHANGETRACK = "Змінився трек номер"
+JBZOO_ADMIN_POSITION_NOTIFICATION_ORDER_CHANGETRACK_DESC = "при кожному наступному зміні трек номера"
JBZOO_ADMIN_POSITION_PAYMENT_LIST = "Доступні способи оплати"
JBZOO_ADMIN_POSITION_PAYMENT_LIST_DESC = "та їх налаштування"
JBZOO_ADMIN_POSITION_PRICEFILTERTMPL_LIST = "Виведення елементів ціни у формі фільтру"
@@ -1486,6 +1490,8 @@ JBZOO_ELEMENT_EMAIL_USER_INFOTYPE_NAME = "Ім'я кори
JBZOO_ELEMENT_EMAIL_USER_INFOTYPE_USERID = "User ID"
JBZOO_ELEMENT_EMAIL_USER_INFOTYPE_USERNAME = "Логін"
JBZOO_ELEMENT_EMAIL_USER_NAME = "Користувач (поточний)"
+JBZOO_ELEMENT_EMAIL_TRACK_NAME = "Замовлення: Трек номер"
+JBZOO_ELEMENT_EMAIL_TRACK_DESC = "Трек номер"
JBZOO_ELEMENT_HOOK_BALANCEREDUCE_DESC = "Змінює залишок товару на складі, зазвичай його ставлять на створення замовлення або його оплату"
JBZOO_ELEMENT_HOOK_BALANCEREDUCE_NAME = "Зменшити залишок"
JBZOO_ELEMENT_HOOK_JBADVERT_DESC = "Запускає виконання елементу JBAdvert в матеріалі, який змінять стан оголошення"
@@ -3267,6 +3273,7 @@ JBZOO_ORDER_MACROS_ORDER_SHIPPING_NAME = "Спосіб доставки"
JBZOO_ORDER_MACROS_ORDER_SHIPPING_STAT = "Статус доставки"
JBZOO_ORDER_MACROS_ORDER_STATUS = "Загальний статус замовлення"
JBZOO_ORDER_MACROS_ORDER_TOTAL = "Сума замовлення"
+JBZOO_ORDER_MACROS_ORDER_TRACK = "Трек номер"
JBZOO_ORDER_MACROS_SHOP_ADDRESS = "Адреса магазину"
JBZOO_ORDER_MACROS_SHOP_CITY = "Місто магазину"
JBZOO_ORDER_MACROS_SHOP_COUNTRY = "Країна магазину"
@@ -3764,22 +3771,26 @@ JBZOO_CONFIG_ZOO_CACHE_WRITABLE_DISABLED_DESC = "Отключается про
JBZOO_ZOO_MEDIA_IS_WRITABLE = "Отключить проверку media папки"
JBZOO_CONFIG_ZOO_MEDIA_IS_WRITABLE = "Отключить проверка прав media папки"
JBZOO_CONFIG_ZOO_MEDIA_IS_WRITABLE_DESC = "Отключается проверка прав на запись media папки"
-JBZOO_CONFIG_MY_ITEMS_HACKS_ON = "Включить свою настройку YML файла"
-JBZOO_CONFIG_MY_ITEMS_HACKS = "Ручная настройка экспорта YML"
-JBZOO_CONFIG_MY_ITEMS_HACKS_EL = "Ручная настройка экспорта YML по Элементу"
-JBZOO_EL_MODE = "Режим работы фильтрации"
-JBZOO_EL_MODE_DESC = "Выбрать режим работы фильтра: 1. не содержит в поле данное значение 2. Только items с данным значением"
-JBZOO_CONFIG_YML_ONLY_MY_LISTIMTES_CUSTOM_RULES_MODE = "Включить свою настройку YML файла"
-JBZOO_CONFIG_YML_ONLY_MY_LISTIMTES_CUSTOM_RULES_MODE_CUSTOM = "Включить опцию настройку YML файла"
-JBZOO_CONFIG_YML_ONLY_MY_PHP_CUSTOM_RULES_MODE_DESC = "Выбрать ДА - чтобы активировать настройки ниже"
-JBZOO_CONFIG_MY_ITEMS_HACKS_LIST = "Экспорт только выборочных items"
-JBZOO_CONFIG_YML_ONLY_MY_LISTITEMS_RULES = "Экспорт только выборочных items"
-JBZOO_CONFIG_YML_ONLY_MY_LISTITEMS_RULES_DESC = "Укажите, через запятую только те материалы, которые должны попасть в YML файл"
-JBZOO_CONFIG_YML_ONLY_MY_ITEMS = "Укажите ELEMENT_ID поля"
-JBZOO_CONFIG_YML_ONLY_MY_ITEMS_DESC = "Укажите ELEMENT_ID по которому будет проверяться значение в выборке в YML файл"
-JBZOO_CONFIG_YML_ONLY_MY_CUSTOMRULES_MODE = "Свое значение в массиве"
-JBZOO_CONFIG_YML_ONLY_MY_CUSTOMRULES_MODE_DESC = "Включите, если Вы будете фильтровать не по элементу checkbox"
-JBZOO_CONFIG_YML_ONLY_MY_ITEMS_VALUE_CUSTOMRULES = "Путь в массиве"
-JBZOO_CONFIG_YML_ONLY_MY_ITEMS_VALUE_CUSTOMRULES_DESC = "Найти в массиве данный путь к значению"
-JBZOO_CONFIG_YML_ONLY_MY_ITEMS_VALUE = "Значение в элементе"
-JBZOO_CONFIG_YML_ONLY_MY_ITEMS_VALUE_DESC = "Проверять значение элемента на данное значение и фильтровать YML файл"
\ No newline at end of file
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; YML custom rules ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+JBZOO_CONFIG_YML_CUSTOM_RULES = "Включити умови пошуку"
+JBZOO_CONFIG_YML_CUSTOM_RULES_DESC = "Виберіть ТАК, щоб активувати настройки нижче"
+JBZOO_CONFIG_YML_CUSTOM_RULES_SEPARATOR = "Включити налаштування YML"
+JBZOO_CONFIG_YML_CUSTOM_ITEMS = "Експорт тільки вибіркових матеріалів"
+JBZOO_CONFIG_YML_CUSTOM_ITEMS_DESC = "Вкажіть, через кому тільки ті матеріали, які повинні потрапити в YML файл"
+JBZOO_CONFIG_YML_CUSTOM_CONDITIONS = "Умови пошуку"
+JBZOO_CONFIG_YML_CUSTOM_CONDITIONS_DESC = "Умови пошуку матеріалів, які повинні бути в стрічці"
+JBZOO_CONFIG_YML_CUSTOM_CONDITIONS_ELEMENTS = "Виберіть елемент"
+JBZOO_CONFIG_YML_CUSTOM_CONDITIONS_ELEMENTS_DESC = "Виберіть елемент"
+JBZOO_CONFIG_YML_CUSTOM_CONDITIONS_MODE = "Виберіть режим порівняння"
+JBZOO_CONFIG_YML_CUSTOM_CONDITIONS_MODE_DESC = "Виберіть режим порівняння"
+JBZOO_CONFIG_YML_CUSTOM_CONDITIONS_MODE_EQUAL = "Так само"
+JBZOO_CONFIG_YML_CUSTOM_CONDITIONS_MODE_NOT_EQUAL = "Не дорівнює"
+JBZOO_CONFIG_YML_CUSTOM_CONDITIONS_VALUE = "Вкажіть значення"
+JBZOO_CONFIG_YML_CUSTOM_CONDITIONS_VALUE_DESC = "Вкажіть значення (повне значення - з фільтра або з рендеру елемента і т.д.)"
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Track Number ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+JBZOO_ORDER_TRACK_TITLE = "Відстеження замовлення"
+JBZOO_ORDER_TRACK = "Трек номер"
diff --git a/src/packages/jbuniversal/jbuniversal/templates/bootstrap/renderer/element/jbxml.php b/src/packages/jbuniversal/jbuniversal/templates/bootstrap/renderer/element/jbxml.php
index 1d3c5a8a7..88103ec7f 100644
--- a/src/packages/jbuniversal/jbuniversal/templates/bootstrap/renderer/element/jbxml.php
+++ b/src/packages/jbuniversal/jbuniversal/templates/bootstrap/renderer/element/jbxml.php
@@ -21,8 +21,14 @@
$label = ($params['altlabel']) ? $params['altlabel'] : $element->getConfig()->get('name');
}
-// render result HTML
-echo ' '
- . $this->app->jbyml->replaceSpecial($element->getSearchData())
- . ''
- . PHP_EOL;
\ No newline at end of file
+$elementData = explode("\n", $element->getSearchData()); // for multiple values elements (select, checkboxes)
+
+if (!empty($elementData)) {
+ foreach ($elementData as $data) {
+ // render result HTML
+ echo ' '
+ . $this->app->jbyml->replaceSpecial($data)
+ . ''
+ . PHP_EOL;
+ }
+}
diff --git a/src/packages/jbuniversal/jbuniversal/templates/bootstrap/renderer/item/ymlexport.php b/src/packages/jbuniversal/jbuniversal/templates/bootstrap/renderer/item/ymlexport.php
index b85ba6581..02230e82c 100644
--- a/src/packages/jbuniversal/jbuniversal/templates/bootstrap/renderer/item/ymlexport.php
+++ b/src/packages/jbuniversal/jbuniversal/templates/bootstrap/renderer/item/ymlexport.php
@@ -18,285 +18,9 @@
$yml = $this->app->jbyml;
-$MyItemsEdit = (int) $this->app->jbconfig->getList('config.yml')['my_items_edit'];
-$MyItemsModeOnlyItemsMode = (int) $this->app->jbconfig->getList('config.yml')['my_items_mode_listimtes'];
-$OnlyMyItemsArr = $this->app->jbconfig->getList('config.yml')['only_my_listitems_rules'];
-$ItemElementMode = (int) $this->app->jbconfig->getList('config.yml')['my_items_mode_listimtes_el'];
-$MyMode = $this->app->jbconfig->getList('config.yml')['my_items_mode_or'];
-$ItemElement = trim($this->app->jbconfig->getList('config.yml')['only_my_items_elements']);
-$ItemElementRulesOption = trim($this->app->jbconfig->getList('config.yml')['only_my_items_customrules']);
-$ItemElementRulesVal = trim($this->app->jbconfig->getList('config.yml')['only_my_items_value']);
-$YmlVal = '';
-
-if ($ItemElementMode == 1 && $MyItemsEdit == 1) {
-
- if (empty($ItemElementRulesOption)) {
- $ItemElementRulesOption = 'option.0';
- }
-
- if ($item->getElement($ItemElement)->data()) {
- $getElYmlCheck = $item->getElement($ItemElement)->data();
- $El_App = $this->app->data->create($getElYmlCheck);
- $YmlVal = $El_App->find($ItemElementRulesOption);
- }
-}
-
?>
-
-id] != 0 || !empty($item_params['price'][$item->id]))) : ?>
-
-
- replaceSpecial($item_params['link'][$item->id]); ?>
-
- id]); ?>
-
- id] > 0) : ?>
- id]; ?>
-
-
- id]; ?>
- id]; ?>
-
- checkPosition('image')) {
- if (is_array($item_params['picture'][$item->id])) {
- foreach ($item_params['picture'][$item->id] as $image) {
- echo '' . $image . " \n ";
- }
- } else {
- echo '' . $item_params['picture'][$item->id] . ' ';
- }
- } ?>
-
- checkPosition('title')) : ?>
- replaceSpecial($this->renderPosition('title')); ?>
-
-
- checkPosition('vendor')) : ?>
- replaceSpecial($this->renderPosition('vendor')) ?>
-
-
- checkPosition('description')) : ?>
- replaceSpecial($this->renderPosition('description')) ?>
-
-
- checkPosition('country_of_origin') && !empty($item_params['country'][$item->id])) : ?>
- replaceSpecial($item_params['country'][$item->id]); ?>
-
-
- checkPosition('properties')) : ?>
- renderPosition('properties', array('style' => 'jbxml')) ?>
-
-
-
-
-
-
-id,explode(',',$OnlyMyItemsArr)))) && ($item_params['price'][$item->id] != 0 || !empty($item_params['price'][$item->id]))) : ?>
-
-
-
- replaceSpecial($item_params['link'][$item->id]); ?>
-
- id]); ?>
-
- id] > 0) : ?>
- id]; ?>
-
-
- id]; ?>
- id]; ?>
-
- checkPosition('image')) {
- if (is_array($item_params['picture'][$item->id])) {
- foreach ($item_params['picture'][$item->id] as $image) {
- echo '' . $image . " \n ";
- }
- } else {
- echo '' . $item_params['picture'][$item->id] . ' ';
- }
- } ?>
-
- checkPosition('title')) : ?>
- replaceSpecial($this->renderPosition('title')); ?>
-
-
- checkPosition('vendor')) : ?>
- replaceSpecial($this->renderPosition('vendor')) ?>
-
-
- checkPosition('description')) : ?>
- replaceSpecial($this->renderPosition('description')) ?>
-
-
- checkPosition('country_of_origin') && !empty($item_params['country'][$item->id])) : ?>
- replaceSpecial($item_params['country'][$item->id]); ?>
-
-
- checkPosition('properties')) : ?>
- renderPosition('properties', array('style' => 'jbxml')) ?>
-
-
-
-
-
-
-
-id,explode(',',$OnlyMyItemsArr)) && $MyMode == 'no' && $YmlVal != $ItemElementRulesVal && ($item_params['price'][$item->id] != 0 || !empty($item_params['price'][$item->id])))) : ?>
-
-
-
- replaceSpecial($item_params['link'][$item->id]); ?>
-
- id]); ?>
-
- id] > 0) : ?>
- id]; ?>
-
-
- id]; ?>
- id]; ?>
-
- checkPosition('image')) {
- if (is_array($item_params['picture'][$item->id])) {
- foreach ($item_params['picture'][$item->id] as $image) {
- echo '' . $image . " \n ";
- }
- } else {
- echo '' . $item_params['picture'][$item->id] . ' ';
- }
- } ?>
-
- checkPosition('title')) : ?>
- replaceSpecial($this->renderPosition('title')); ?>
-
-
- checkPosition('vendor')) : ?>
- replaceSpecial($this->renderPosition('vendor')) ?>
-
-
- checkPosition('description')) : ?>
- replaceSpecial($this->renderPosition('description')) ?>
-
-
- checkPosition('country_of_origin') && !empty($item_params['country'][$item->id])) : ?>
- replaceSpecial($item_params['country'][$item->id]); ?>
-
-
- checkPosition('properties')) : ?>
- renderPosition('properties', array('style' => 'jbxml')) ?>
-
-
-
-
-
-
-id,explode(',',$OnlyMyItemsArr)) && $MyMode == 'and' && $YmlVal == $ItemElementRulesVal && ($item_params['price'][$item->id] != 0 || !empty($item_params['price'][$item->id])))) : ?>
-
-
-
- replaceSpecial($item_params['link'][$item->id]); ?>
-
- id]); ?>
-
- id] > 0) : ?>
- id]; ?>
-
-
- id]; ?>
- id]; ?>
-
- checkPosition('image')) {
- if (is_array($item_params['picture'][$item->id])) {
- foreach ($item_params['picture'][$item->id] as $image) {
- echo '' . $image . " \n ";
- }
- } else {
- echo '' . $item_params['picture'][$item->id] . ' ';
- }
- } ?>
-
- checkPosition('title')) : ?>
- replaceSpecial($this->renderPosition('title')); ?>
-
-
- checkPosition('vendor')) : ?>
- replaceSpecial($this->renderPosition('vendor')) ?>
-
-
- checkPosition('description')) : ?>
- replaceSpecial($this->renderPosition('description')) ?>
-
-
- checkPosition('country_of_origin') && !empty($item_params['country'][$item->id])) : ?>
- replaceSpecial($item_params['country'][$item->id]); ?>
-
-
- checkPosition('properties')) : ?>
- renderPosition('properties', array('style' => 'jbxml')) ?>
-
-
-
-
-
-
-
-id] != 0 || !empty($item_params['price'][$item->id])))) : ?>
-
-
-
- replaceSpecial($item_params['link'][$item->id]); ?>
-
- id]); ?>
-
- id] > 0) : ?>
- id]; ?>
-
-
- id]; ?>
- id]; ?>
-
- checkPosition('image')) {
- if (is_array($item_params['picture'][$item->id])) {
- foreach ($item_params['picture'][$item->id] as $image) {
- echo '' . $image . " \n ";
- }
- } else {
- echo '' . $item_params['picture'][$item->id] . ' ';
- }
- } ?>
-
- checkPosition('title')) : ?>
- replaceSpecial($this->renderPosition('title')); ?>
-
-
- checkPosition('vendor')) : ?>
- replaceSpecial($this->renderPosition('vendor')) ?>
-
-
- checkPosition('description')) : ?>
- replaceSpecial($this->renderPosition('description')) ?>
-
-
- checkPosition('country_of_origin') && !empty($item_params['country'][$item->id])) : ?>
- replaceSpecial($item_params['country'][$item->id]); ?>
-
-
- checkPosition('properties')) : ?>
- renderPosition('properties', array('style' => 'jbxml')) ?>
-
-
-
-
-
-
-id] != 0 || !empty($item_params['price'][$item->id])))) : ?>
-
+id] != 0 || !empty($item_params['price'][$item->id])) : ?>
@@ -341,4 +65,4 @@
renderPosition('properties', array('style' => 'jbxml')) ?>
-
\ No newline at end of file
diff --git a/src/packages/jbuniversal/jbuniversal/templates/catalog/renderer/element/jbxml.php b/src/packages/jbuniversal/jbuniversal/templates/catalog/renderer/element/jbxml.php
index 1d3c5a8a7..88103ec7f 100644
--- a/src/packages/jbuniversal/jbuniversal/templates/catalog/renderer/element/jbxml.php
+++ b/src/packages/jbuniversal/jbuniversal/templates/catalog/renderer/element/jbxml.php
@@ -21,8 +21,14 @@
$label = ($params['altlabel']) ? $params['altlabel'] : $element->getConfig()->get('name');
}
-// render result HTML
-echo ' '
- . $this->app->jbyml->replaceSpecial($element->getSearchData())
- . ''
- . PHP_EOL;
\ No newline at end of file
+$elementData = explode("\n", $element->getSearchData()); // for multiple values elements (select, checkboxes)
+
+if (!empty($elementData)) {
+ foreach ($elementData as $data) {
+ // render result HTML
+ echo ' '
+ . $this->app->jbyml->replaceSpecial($data)
+ . ''
+ . PHP_EOL;
+ }
+}
diff --git a/src/packages/jbuniversal/jbuniversal/templates/catalog/renderer/item/ymlexport.php b/src/packages/jbuniversal/jbuniversal/templates/catalog/renderer/item/ymlexport.php
index b85ba6581..dfc248aed 100644
--- a/src/packages/jbuniversal/jbuniversal/templates/catalog/renderer/item/ymlexport.php
+++ b/src/packages/jbuniversal/jbuniversal/templates/catalog/renderer/item/ymlexport.php
@@ -18,33 +18,9 @@
$yml = $this->app->jbyml;
-$MyItemsEdit = (int) $this->app->jbconfig->getList('config.yml')['my_items_edit'];
-$MyItemsModeOnlyItemsMode = (int) $this->app->jbconfig->getList('config.yml')['my_items_mode_listimtes'];
-$OnlyMyItemsArr = $this->app->jbconfig->getList('config.yml')['only_my_listitems_rules'];
-$ItemElementMode = (int) $this->app->jbconfig->getList('config.yml')['my_items_mode_listimtes_el'];
-$MyMode = $this->app->jbconfig->getList('config.yml')['my_items_mode_or'];
-$ItemElement = trim($this->app->jbconfig->getList('config.yml')['only_my_items_elements']);
-$ItemElementRulesOption = trim($this->app->jbconfig->getList('config.yml')['only_my_items_customrules']);
-$ItemElementRulesVal = trim($this->app->jbconfig->getList('config.yml')['only_my_items_value']);
-$YmlVal = '';
-
-if ($ItemElementMode == 1 && $MyItemsEdit == 1) {
-
- if (empty($ItemElementRulesOption)) {
- $ItemElementRulesOption = 'option.0';
- }
-
- if ($item->getElement($ItemElement)->data()) {
- $getElYmlCheck = $item->getElement($ItemElement)->data();
- $El_App = $this->app->data->create($getElYmlCheck);
- $YmlVal = $El_App->find($ItemElementRulesOption);
- }
-}
-
?>
-
-id] != 0 || !empty($item_params['price'][$item->id]))) : ?>
+id] != 0 || !empty($item_params['price'][$item->id])) : ?>
@@ -90,255 +66,3 @@
-
-
-
-id,explode(',',$OnlyMyItemsArr)))) && ($item_params['price'][$item->id] != 0 || !empty($item_params['price'][$item->id]))) : ?>
-
-
-
- replaceSpecial($item_params['link'][$item->id]); ?>
-
- id]); ?>
-
- id] > 0) : ?>
- id]; ?>
-
-
- id]; ?>
- id]; ?>
-
- checkPosition('image')) {
- if (is_array($item_params['picture'][$item->id])) {
- foreach ($item_params['picture'][$item->id] as $image) {
- echo '' . $image . " \n ";
- }
- } else {
- echo '' . $item_params['picture'][$item->id] . ' ';
- }
- } ?>
-
- checkPosition('title')) : ?>
- replaceSpecial($this->renderPosition('title')); ?>
-
-
- checkPosition('vendor')) : ?>
- replaceSpecial($this->renderPosition('vendor')) ?>
-
-
- checkPosition('description')) : ?>
- replaceSpecial($this->renderPosition('description')) ?>
-
-
- checkPosition('country_of_origin') && !empty($item_params['country'][$item->id])) : ?>
- replaceSpecial($item_params['country'][$item->id]); ?>
-
-
- checkPosition('properties')) : ?>
- renderPosition('properties', array('style' => 'jbxml')) ?>
-
-
-
-
-
-
-
-id,explode(',',$OnlyMyItemsArr)) && $MyMode == 'no' && $YmlVal != $ItemElementRulesVal && ($item_params['price'][$item->id] != 0 || !empty($item_params['price'][$item->id])))) : ?>
-
-
-
- replaceSpecial($item_params['link'][$item->id]); ?>
-
- id]); ?>
-
- id] > 0) : ?>
- id]; ?>
-
-
- id]; ?>
- id]; ?>
-
- checkPosition('image')) {
- if (is_array($item_params['picture'][$item->id])) {
- foreach ($item_params['picture'][$item->id] as $image) {
- echo '' . $image . " \n ";
- }
- } else {
- echo '' . $item_params['picture'][$item->id] . ' ';
- }
- } ?>
-
- checkPosition('title')) : ?>
- replaceSpecial($this->renderPosition('title')); ?>
-
-
- checkPosition('vendor')) : ?>
- replaceSpecial($this->renderPosition('vendor')) ?>
-
-
- checkPosition('description')) : ?>
- replaceSpecial($this->renderPosition('description')) ?>
-
-
- checkPosition('country_of_origin') && !empty($item_params['country'][$item->id])) : ?>
- replaceSpecial($item_params['country'][$item->id]); ?>
-
-
- checkPosition('properties')) : ?>
- renderPosition('properties', array('style' => 'jbxml')) ?>
-
-
-
-
-
-
-id,explode(',',$OnlyMyItemsArr)) && $MyMode == 'and' && $YmlVal == $ItemElementRulesVal && ($item_params['price'][$item->id] != 0 || !empty($item_params['price'][$item->id])))) : ?>
-
-
-
- replaceSpecial($item_params['link'][$item->id]); ?>
-
- id]); ?>
-
- id] > 0) : ?>
- id]; ?>
-
-
- id]; ?>
- id]; ?>
-
- checkPosition('image')) {
- if (is_array($item_params['picture'][$item->id])) {
- foreach ($item_params['picture'][$item->id] as $image) {
- echo '' . $image . " \n ";
- }
- } else {
- echo '' . $item_params['picture'][$item->id] . ' ';
- }
- } ?>
-
- checkPosition('title')) : ?>
- replaceSpecial($this->renderPosition('title')); ?>
-
-
- checkPosition('vendor')) : ?>
- replaceSpecial($this->renderPosition('vendor')) ?>
-
-
- checkPosition('description')) : ?>
- replaceSpecial($this->renderPosition('description')) ?>
-
-
- checkPosition('country_of_origin') && !empty($item_params['country'][$item->id])) : ?>
- replaceSpecial($item_params['country'][$item->id]); ?>
-
-
- checkPosition('properties')) : ?>
- renderPosition('properties', array('style' => 'jbxml')) ?>
-
-
-
-
-
-
-
-id] != 0 || !empty($item_params['price'][$item->id])))) : ?>
-
-
-
- replaceSpecial($item_params['link'][$item->id]); ?>
-
- id]); ?>
-
- id] > 0) : ?>
- id]; ?>
-
-
- id]; ?>
- id]; ?>
-
- checkPosition('image')) {
- if (is_array($item_params['picture'][$item->id])) {
- foreach ($item_params['picture'][$item->id] as $image) {
- echo '' . $image . " \n ";
- }
- } else {
- echo '' . $item_params['picture'][$item->id] . ' ';
- }
- } ?>
-
- checkPosition('title')) : ?>
- replaceSpecial($this->renderPosition('title')); ?>
-
-
- checkPosition('vendor')) : ?>
- replaceSpecial($this->renderPosition('vendor')) ?>
-
-
- checkPosition('description')) : ?>
- replaceSpecial($this->renderPosition('description')) ?>
-
-
- checkPosition('country_of_origin') && !empty($item_params['country'][$item->id])) : ?>
- replaceSpecial($item_params['country'][$item->id]); ?>
-
-
- checkPosition('properties')) : ?>
- renderPosition('properties', array('style' => 'jbxml')) ?>
-
-
-
-
-
-
-id] != 0 || !empty($item_params['price'][$item->id])))) : ?>
-
-
-
- replaceSpecial($item_params['link'][$item->id]); ?>
-
- id]); ?>
-
- id] > 0) : ?>
- id]; ?>
-
-
- id]; ?>
- id]; ?>
-
- checkPosition('image')) {
- if (is_array($item_params['picture'][$item->id])) {
- foreach ($item_params['picture'][$item->id] as $image) {
- echo '' . $image . " \n ";
- }
- } else {
- echo '' . $item_params['picture'][$item->id] . ' ';
- }
- } ?>
-
- checkPosition('title')) : ?>
- replaceSpecial($this->renderPosition('title')); ?>
-
-
- checkPosition('vendor')) : ?>
- replaceSpecial($this->renderPosition('vendor')) ?>
-
-
- checkPosition('description')) : ?>
- replaceSpecial($this->renderPosition('description')) ?>
-
-
- checkPosition('country_of_origin') && !empty($item_params['country'][$item->id])) : ?>
- replaceSpecial($item_params['country'][$item->id]); ?>
-
-
- checkPosition('properties')) : ?>
- renderPosition('properties', array('style' => 'jbxml')) ?>
-
-
-getConfig()->get('name');
}
-// render result HTML
-echo ' '
- . $this->app->jbyml->replaceSpecial($element->getSearchData())
- . ''
- . PHP_EOL;
\ No newline at end of file
+$elementData = explode("\n", $element->getSearchData()); // for multiple values elements (select, checkboxes)
+
+if (!empty($elementData)) {
+ foreach ($elementData as $data) {
+ // render result HTML
+ echo ' '
+ . $this->app->jbyml->replaceSpecial($data)
+ . ''
+ . PHP_EOL;
+ }
+}
diff --git a/src/packages/jbuniversal/jbuniversal/templates/uikit/renderer/item/ymlexport.php b/src/packages/jbuniversal/jbuniversal/templates/uikit/renderer/item/ymlexport.php
index b85ba6581..dfc248aed 100644
--- a/src/packages/jbuniversal/jbuniversal/templates/uikit/renderer/item/ymlexport.php
+++ b/src/packages/jbuniversal/jbuniversal/templates/uikit/renderer/item/ymlexport.php
@@ -18,33 +18,9 @@
$yml = $this->app->jbyml;
-$MyItemsEdit = (int) $this->app->jbconfig->getList('config.yml')['my_items_edit'];
-$MyItemsModeOnlyItemsMode = (int) $this->app->jbconfig->getList('config.yml')['my_items_mode_listimtes'];
-$OnlyMyItemsArr = $this->app->jbconfig->getList('config.yml')['only_my_listitems_rules'];
-$ItemElementMode = (int) $this->app->jbconfig->getList('config.yml')['my_items_mode_listimtes_el'];
-$MyMode = $this->app->jbconfig->getList('config.yml')['my_items_mode_or'];
-$ItemElement = trim($this->app->jbconfig->getList('config.yml')['only_my_items_elements']);
-$ItemElementRulesOption = trim($this->app->jbconfig->getList('config.yml')['only_my_items_customrules']);
-$ItemElementRulesVal = trim($this->app->jbconfig->getList('config.yml')['only_my_items_value']);
-$YmlVal = '';
-
-if ($ItemElementMode == 1 && $MyItemsEdit == 1) {
-
- if (empty($ItemElementRulesOption)) {
- $ItemElementRulesOption = 'option.0';
- }
-
- if ($item->getElement($ItemElement)->data()) {
- $getElYmlCheck = $item->getElement($ItemElement)->data();
- $El_App = $this->app->data->create($getElYmlCheck);
- $YmlVal = $El_App->find($ItemElementRulesOption);
- }
-}
-
?>
-
-id] != 0 || !empty($item_params['price'][$item->id]))) : ?>
+id] != 0 || !empty($item_params['price'][$item->id])) : ?>
@@ -90,255 +66,3 @@
-
-
-
-id,explode(',',$OnlyMyItemsArr)))) && ($item_params['price'][$item->id] != 0 || !empty($item_params['price'][$item->id]))) : ?>
-
-
-
- replaceSpecial($item_params['link'][$item->id]); ?>
-
- id]); ?>
-
- id] > 0) : ?>
- id]; ?>
-
-
- id]; ?>
- id]; ?>
-
- checkPosition('image')) {
- if (is_array($item_params['picture'][$item->id])) {
- foreach ($item_params['picture'][$item->id] as $image) {
- echo '' . $image . " \n ";
- }
- } else {
- echo '' . $item_params['picture'][$item->id] . ' ';
- }
- } ?>
-
- checkPosition('title')) : ?>
- replaceSpecial($this->renderPosition('title')); ?>
-
-
- checkPosition('vendor')) : ?>
- replaceSpecial($this->renderPosition('vendor')) ?>
-
-
- checkPosition('description')) : ?>
- replaceSpecial($this->renderPosition('description')) ?>
-
-
- checkPosition('country_of_origin') && !empty($item_params['country'][$item->id])) : ?>
- replaceSpecial($item_params['country'][$item->id]); ?>
-
-
- checkPosition('properties')) : ?>
- renderPosition('properties', array('style' => 'jbxml')) ?>
-
-
-
-
-
-
-
-id,explode(',',$OnlyMyItemsArr)) && $MyMode == 'no' && $YmlVal != $ItemElementRulesVal && ($item_params['price'][$item->id] != 0 || !empty($item_params['price'][$item->id])))) : ?>
-
-
-
- replaceSpecial($item_params['link'][$item->id]); ?>
-
- id]); ?>
-
- id] > 0) : ?>
- id]; ?>
-
-
- id]; ?>
- id]; ?>
-
- checkPosition('image')) {
- if (is_array($item_params['picture'][$item->id])) {
- foreach ($item_params['picture'][$item->id] as $image) {
- echo '' . $image . " \n ";
- }
- } else {
- echo '' . $item_params['picture'][$item->id] . ' ';
- }
- } ?>
-
- checkPosition('title')) : ?>
- replaceSpecial($this->renderPosition('title')); ?>
-
-
- checkPosition('vendor')) : ?>
- replaceSpecial($this->renderPosition('vendor')) ?>
-
-
- checkPosition('description')) : ?>
- replaceSpecial($this->renderPosition('description')) ?>
-
-
- checkPosition('country_of_origin') && !empty($item_params['country'][$item->id])) : ?>
- replaceSpecial($item_params['country'][$item->id]); ?>
-
-
- checkPosition('properties')) : ?>
- renderPosition('properties', array('style' => 'jbxml')) ?>
-
-
-
-
-
-
-id,explode(',',$OnlyMyItemsArr)) && $MyMode == 'and' && $YmlVal == $ItemElementRulesVal && ($item_params['price'][$item->id] != 0 || !empty($item_params['price'][$item->id])))) : ?>
-
-
-
- replaceSpecial($item_params['link'][$item->id]); ?>
-
- id]); ?>
-
- id] > 0) : ?>
- id]; ?>
-
-
- id]; ?>
- id]; ?>
-
- checkPosition('image')) {
- if (is_array($item_params['picture'][$item->id])) {
- foreach ($item_params['picture'][$item->id] as $image) {
- echo '' . $image . " \n ";
- }
- } else {
- echo '' . $item_params['picture'][$item->id] . ' ';
- }
- } ?>
-
- checkPosition('title')) : ?>
- replaceSpecial($this->renderPosition('title')); ?>
-
-
- checkPosition('vendor')) : ?>
- replaceSpecial($this->renderPosition('vendor')) ?>
-
-
- checkPosition('description')) : ?>
- replaceSpecial($this->renderPosition('description')) ?>
-
-
- checkPosition('country_of_origin') && !empty($item_params['country'][$item->id])) : ?>
- replaceSpecial($item_params['country'][$item->id]); ?>
-
-
- checkPosition('properties')) : ?>
- renderPosition('properties', array('style' => 'jbxml')) ?>
-
-
-
-
-
-
-
-id] != 0 || !empty($item_params['price'][$item->id])))) : ?>
-
-
-
- replaceSpecial($item_params['link'][$item->id]); ?>
-
- id]); ?>
-
- id] > 0) : ?>
- id]; ?>
-
-
- id]; ?>
- id]; ?>
-
- checkPosition('image')) {
- if (is_array($item_params['picture'][$item->id])) {
- foreach ($item_params['picture'][$item->id] as $image) {
- echo '' . $image . " \n ";
- }
- } else {
- echo '' . $item_params['picture'][$item->id] . ' ';
- }
- } ?>
-
- checkPosition('title')) : ?>
- replaceSpecial($this->renderPosition('title')); ?>
-
-
- checkPosition('vendor')) : ?>
- replaceSpecial($this->renderPosition('vendor')) ?>
-
-
- checkPosition('description')) : ?>
- replaceSpecial($this->renderPosition('description')) ?>
-
-
- checkPosition('country_of_origin') && !empty($item_params['country'][$item->id])) : ?>
- replaceSpecial($item_params['country'][$item->id]); ?>
-
-
- checkPosition('properties')) : ?>
- renderPosition('properties', array('style' => 'jbxml')) ?>
-
-
-
-
-
-
-id] != 0 || !empty($item_params['price'][$item->id])))) : ?>
-
-
-
- replaceSpecial($item_params['link'][$item->id]); ?>
-
- id]); ?>
-
- id] > 0) : ?>
- id]; ?>
-
-
- id]; ?>
- id]; ?>
-
- checkPosition('image')) {
- if (is_array($item_params['picture'][$item->id])) {
- foreach ($item_params['picture'][$item->id] as $image) {
- echo '' . $image . " \n ";
- }
- } else {
- echo '' . $item_params['picture'][$item->id] . ' ';
- }
- } ?>
-
- checkPosition('title')) : ?>
- replaceSpecial($this->renderPosition('title')); ?>
-
-
- checkPosition('vendor')) : ?>
- replaceSpecial($this->renderPosition('vendor')) ?>
-
-
- checkPosition('description')) : ?>
- replaceSpecial($this->renderPosition('description')) ?>
-
-
- checkPosition('country_of_origin') && !empty($item_params['country'][$item->id])) : ?>
- replaceSpecial($item_params['country'][$item->id]); ?>
-
-
- checkPosition('properties')) : ?>
- renderPosition('properties', array('style' => 'jbxml')) ?>
-
-
-Copyright (C) JBZoo.com, All rights reserved.
admin@jbzoo.com
http://jbzoo.com
- 4.10.3
+ 4.11.0
GPL-2.0
jbzoo
diff --git a/update.xml b/update.xml
index 79a48c851..493c6b098 100644
--- a/update.xml
+++ b/update.xml
@@ -20,11 +20,11 @@
GPL-2.0
JBZoo Community Edition
package
- 4.0.6
+ 4.11.0
https://github.com/JBZoo/JBZoo/
-
+